android自定义控件之模仿优酷菜单

去年的优酷HD版有过这样一种菜单,如下图:

应用打开之后,先是三个弧形的三级菜单,点击实体键menu之后,这三个菜单依次旋转退出,再点击实体键menu之后,一级菜单会旋转进入,点击一级菜单,二级菜单旋转进入,点击二级菜单的menu键,三级菜单旋转进入,再次点击二级菜单的旋转键,三级菜单又会旋转退出,这时再点击一级菜单,二级菜单退出,最后点击实体menu键,一级菜单退出。

总体来说实现这样的功能:

(1)点击实体menu键时,如果界面上有菜单显示,不管有几个,全部依次退出,如果界面上没有菜单显示,则显示一级菜单。

(2)点击一级菜单的home键时,如果此时界面只有一级菜单,则显示二级菜单,否则让除了一级菜单外的菜单全都依次退出。

(3)点击二级菜单的menu键时,如果三级菜单已经显示,则让它旋转退出,如果三级菜单未显示则让它旋转进入。

好了,今天我们主要实现上述效果。

先来看布局文件

<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.customwidget.MainActivity" >

    <RelativeLayout
        android:id="@+id/menu_level1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level1" >

        <ImageButton
            android:id="@+id/level1_home"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:layout_marginBottom="10dp"
            android:background="@drawable/icon_home"
            android:onClick="myClick" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/menu_level2"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level2" >

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="15dp"
            android:background="@drawable/icon_search" />

        <ImageButton
            android:id="@+id/level2_menu"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="7dp"
            android:background="@drawable/icon_menu"
            android:onClick="myClick" />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="15dp"
            android:background="@drawable/icon_myyouku" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/menu_level3"
        android:layout_width="320dp"
        android:layout_height="162dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level3" >

        <ImageButton
            android:id="@+id/level3_channel1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="12dp"
            android:background="@drawable/channel1" />

        <ImageButton
            android:id="@+id/level3_channel2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/level3_channel1"
            android:layout_marginBottom="17dp"
            android:layout_marginLeft="-5dp"
            android:layout_toRightOf="@id/level3_channel1"
            android:background="@drawable/channel2" />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/level3_channel2"
            android:layout_marginBottom="15dp"
            android:layout_marginLeft="13dp"
            android:layout_toRightOf="@id/level3_channel2"
            android:background="@drawable/channel3" />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="10dp"
            android:background="@drawable/channel4" />

        <ImageButton
            android:id="@+id/level3_channel7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="12dp"
            android:background="@drawable/channel7" />

        <ImageButton
            android:id="@+id/level3_channel6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/level3_channel7"
            android:layout_marginBottom="17dp"
            android:layout_marginRight="-5dp"
            android:layout_toLeftOf="@id/level3_channel7"
            android:background="@drawable/channel6" />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/level3_channel6"
            android:layout_marginBottom="15dp"
            android:layout_marginRight="13dp"
            android:layout_toLeftOf="@id/level3_channel6"
            android:background="@drawable/channel5" />
    </RelativeLayout>

</RelativeLayout>

这里是一个相对布局中嵌套了三个相对布局,嵌套的第一个相对布局负责显示一级菜单,嵌套的第二个相对布局负责显示二级菜单,嵌套的第三个相对布局负责显示三级菜单。三个不同层次的菜单的背景都是弧形。我们通过指定具体的宽高来使三个层次的菜单具有不同的大小。

效果如下:

再看看MainActivity.java

/**
 * 模仿优酷菜单
 * 2015年5月19日
 */
public class MainActivity extends Activity {

    //分别拿到不同等级的菜单
    private RelativeLayout lv1;
    private RelativeLayout lv2;
    private RelativeLayout lv3;
    private Animation animation;
    //各级菜单是否显示,默认全都显示
    private boolean isDisplaylv1 = true;
    private boolean isDisplaylv2 = true;
    private boolean isDisplaylv3 = true;
    //动画是否正在执行,默认动画没有执行
    private boolean isAnimationRunning = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv1 = (RelativeLayout) this.findViewById(R.id.menu_level1);
        lv2 = (RelativeLayout) this.findViewById(R.id.menu_level2);
        lv3 = (RelativeLayout) this.findViewById(R.id.menu_level3);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //如果动画正在执行,则不处理此事件
        if (isAnimationRunning)
            return super.onKeyDown(keyCode, event);
        //如果点击的是菜单键
        if (keyCode == KeyEvent.KEYCODE_MENU) {
            //如果一级菜单已经显示,判断二级菜单是否显示
            if (isDisplaylv1) {
                //设置动画启动延迟时间
                int startOffset = 0;
                //如果二级菜单已经显示,判断三级菜单是否显示,然后退出二级菜单
                if (isDisplaylv2) {
                    if (isDisplaylv3) {
                        //如果三级菜单已经显示,执行退出动画
                        exitAnimation(lv3, startOffset);
                        //三级菜单退出动画执行完毕之后,动画的启动时间延迟500ms
                        startOffset += 500;
                        isDisplaylv3 = !isDisplaylv3;
                    }
                    //二级菜单退出,此时startOffset=500,即动画启动时间延迟500ms
                    exitAnimation(lv2, startOffset);
                    //二级菜单退出动画执行完毕之后,动画的启动时间延迟500ms
                    startOffset += 500;
                    isDisplaylv2 = !isDisplaylv2;
                }
                //一级菜单退出,此时startOffset=1000,即动画启动时间延迟1000ms
                exitAnimation(lv1, startOffset);
            //如果一级菜单未显示,则一级菜单进入
            } else {
                enterAnimation(lv1);
            }
            isDisplaylv1 = !isDisplaylv1;
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    public void myClick(View v) {
        //如果动画正在执行,则不处理此事件
        if (isAnimationRunning)
            return;
        switch (v.getId()) {
        /**
         * 当点击二级菜单的menu时,如果三级菜单已经显示,则执行退出动画,
         * 否则执行进入动画
         */
        case R.id.level2_menu:
            if (isDisplaylv3) {
                exitAnimation(lv3, 0);
            } else {
                enterAnimation(lv3);
            }
            isDisplaylv3 = !isDisplaylv3;
            break;
        case R.id.level1_home:
            // 如果二级菜单已经显示,再判断三级菜单是否显示
            if (isDisplaylv2) {
                //通过设置动画启动延迟时间,来实现动画依次退出效果
                int startOffset = 0;
                // 如果三级菜单也显示了,则让他们依次退出
                if (isDisplaylv3) {
                    exitAnimation(lv3, startOffset);
                    startOffset = 700;
                    isDisplaylv3 = !isDisplaylv3;
                }
                exitAnimation(lv2, startOffset);
                isDisplaylv2 = !isDisplaylv2;
                // 如果二级菜单没有显示,就让二级菜单显示出来
            } else {
                enterAnimation(lv2);
                isDisplaylv2 = !isDisplaylv2;
            }
            break;
        }
    }

    /**
     * 退出动画
     * @param layout 执行动画的布局文件
     * @param startOffset 动画启动的延迟时间
     */
    public void exitAnimation(RelativeLayout layout, long startOffset) {
        animation = AnimationUtils.loadAnimation(this, R.anim.exit_menu);
        animation.setFillAfter(true);
        animation.setStartOffset(startOffset);
        animation.setAnimationListener(new MyAnimationListener());
        layout.startAnimation(animation);
    }

    /**
     * 进入动画
     * @param layout 执行动画的布局文件
     */
    public void enterAnimation(RelativeLayout layout) {
        animation = AnimationUtils.loadAnimation(this, R.anim.enter_menu);
        animation.setFillAfter(true);
        animation.setAnimationListener(new MyAnimationListener());
        layout.startAnimation(animation);
    }

    /**
     * 判断动画是否正在执行
     * @author 王松
     *
     */
    private class MyAnimationListener implements AnimationListener {

        //动画开始执行
        @Override
        public void onAnimationStart(Animation animation) {
            isAnimationRunning = true;
        }

        //动画执行结束
        @Override
        public void onAnimationEnd(Animation animation) {
            isAnimationRunning = false;
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

    }
}

代码中注释已经写的很详细了,这里不再赘述。最后在给大家看看两个动画文件:

enter_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">
    <rotate
        android:duration="1000"
        android:fromDegrees="-180"
        android:toDegrees="0"
        android:pivotX="50%"
        android:pivotY="100%" />
</set>

exit_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">
    <rotate
        android:duration="1000"
        android:fromDegrees="0"
        android:toDegrees="-180"
        android:pivotX="50%"
        android:pivotY="100%" />
</set>

关于动画如果不太懂可以看这里android之tween动画详解android之frame动画详解。。

本项目完整代码下载

时间: 2024-11-12 11:53:40

android自定义控件之模仿优酷菜单的相关文章

模仿优酷菜单

转载注明出处:    http://blog.csdn.net/forwardyzk/article/details/42554691 在开发中,会使用菜单,现在模拟一下优酷的菜单效果. 其实效果都是由android基本的动画效果组成的,在合适的时间显示对应的动画,即可展示出不一样的效果. 思路: 1.自定义类RotateMenuView继承RelativeLayout. 2.在需要加载的布局文件中,添加对应的菜单View. 3.定义菜单进入和出去的动画效果,使用旋转动画,进来顺时针,出去的时候

自定义View(一)-ViewGroup实现优酷菜单

自定义View的第一个学习案例 ViewGroup是自动以View中比较常用也比较简单的一种方式,通过组合现有的UI控件,绘制出一个全新的View 效果如下: 主类实现如下: package com.demo.youku; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.K

Android优酷菜单组件自定义

主要做的就是模仿优酷手机客户端的底部菜单控件的实现.先来几张图片,点击中间的home,显示二级菜单,点击二级菜单的menu,显示三级菜单. 这是实现起来最简单的一个布局,但是从中学会了自定义动画和一些布局的基本知识,从中还是收获很大的. 首先是定义布局文件,三个菜单条其实就是三个relativelayout,level1,level2,level3,然后每个菜单条中的小标题就加到对应的相对布局中. <RelativeLayout xmlns:android="http://schemas.

HTML5 CSS3 诱人的实例 :模仿优酷视频截图功能

一般的视频网站对于用户上传的视频,在用户上传完成后,可以对播放的视频进行截图,然后作为视频的展示图.项目中也可以引入这样的功能给用户一种不错的体验,而不是让用户额外上传一张展示图. 效果图: 看起来还是很不错,下面我给大家分析下,极其核心代码很简单: _canvas = document.createElement("canvas"); _ctx = _canvas.getContext("2d"); _ctx.fillStyle = '#ffffff'; _ctx

Android仿优酷菜单效果

一.效果图 二.主要的技术点 1.RelativeLayout布局 2.RotateAnimation旋转动画 三.需求 1.点击二级菜单中的“menu”键控制三级菜单的进入和退出动画效果: 2.点击一级菜单中的“home”键控制二级和三级菜单的进入和退出效果: 3.点击手机上的菜单键控制一级.二级和三级菜单的进入和退出效果. 四.实例代码 1.布局文件:activity_main.xml <RelativeLayout xmlns:android="http://schemas.andr

优酷菜单

Android中RelativeLayout各个属性的含义 android:layout_above="@id/xxx"  --将控件置于给定ID控件之上android:layout_below="@id/xxx"  --将控件置于给定ID控件之下 android:layout_toLeftOf="@id/xxx"  --将控件的右边缘和给定ID控件的左边缘对齐android:layout_toRightOf="@id/xxx"

android 使用webview访问优酷无法播放视频的问题

在代码中加入 webview.getSettings().setJavaScriptEnabled(true);//支持js //webview.getSettings().setPluginsEnabled(true);//设置webview支持插件,已废弃 webview.settings.setPluginState(PluginState.ON); //设置webview支持插件 同时要在Manifest配置文件的application中加入 android:hardwareAccele

Android自定义控件:进度条的四种实现方式

前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源码在最后) 最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧. 一.控件介绍: 进度条在App中非常常见,例如下载进度.加载图片.打开文章.打开网页等等--都需要这么一个效果让用户知道我们的App正在读取,以构造良好的

Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)(源码 + Demo)

Progress Wheel为GitHub热门项目,作者是:Todd-Davies,项目地址: https://github.com/Todd-Davies/ProgressWheel 前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源码在最后) 最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想