Android高手进阶教程(二十六)之---Android超仿Path菜单的功能实现!

Hi~大家好,出来创业快3个月了,一切还不错,前一段时间用了业余时间搞了个问答类网站YQMA.想做中国的stackoverflow,哈哈,只是YY下,希望大家多多支持!

好了,今天给大家分享的是Path菜单的简单实现,可以支持自定义方向(左上,右上,右下,左下),并且可以自定义菜单的个数,难点就是菜单的摆放位置(动态设置margin),还有动画的实现,其实动画只是简单用了个TranslateAnimation,N个菜单一起移动的时候感觉很cool~

这里也用到了自定义标签,这里不懂的童鞋可以看我 Android高手进阶教程(四)之----Android 中自定义属性(attr.xml,TypedArray)的使用! 这篇文章.好了废话不多说了,

首先创建一个android工程命名为PathTest.目录结构如下图:

第二步:在values文件夹下新建一个attrs.xml文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="PathMenuView">
    <attr name="position">
      <enum name="left_top" value="0"></enum>
      <enum name="right_top" value="1"></enum>
      <enum name="right_bottom" value="2"></enum>
      <enum name="left_bottom" value="3"></enum>
    </attr>
  </declare-styleable>
</resources> 

第三步:新建一个PathMenuView.Java这个就是我们自定义的Path菜单控件,代码如下:

package com.tutor.path; 

import androidcontentContext;
import androidcontentresTypedArray;
import androidutilAttributeSet;
import androidviewGravity;
import androidviewView;
import androidviewViewGroup;
import androidviewanimationAnimation;
import androidviewanimationAnticipateInterpolator;
import androidviewanimationOvershootInterpolator;
import androidviewanimationTranslateAnimation;
import androidwidgetFrameLayout;
import androidwidgetImageView; 

/**
 * @author frankiewei
 * 超级仿path菜单
 * position定义菜单的位置,目前支持:左上;右上;右下;左下四个方向。
 * menuResIds定义出现的菜单的资源ID
 */
public class PathMenuView extends FrameLayout { 

  private static final int LEFT_TOP = 0; 

  private static final int RIGHT_TOP = 1; 

  private static final int RIGHT_BOTTOM = 2; 

  private static final int LEFT_BOTTOM = 3; 

  /**
   * 默认的位置是在右下角
   */
  private int position = 3; 

  /**
   * 那个圆形菜单
   */
  private ImageView mHome; 

  /**
   * 上下文
   */
  private Context mContext; 

  /**
   * 设备的宽度
   */
  private int mWIDTH = 0; 

  /**
   * 设备的高度
   */
  private int mHEIGHT = 0; 

  /**
   * 设备的density
   */
  private float mDensity; 

  /**
   * 菜单是否显示
   */
  private boolean bMenuShow; 

  private static int xOffset   = 15;
  private static int yOffset   = -13; 

  /**
   * 菜单的资源个数
   */
  private int[] menuResIds = {Rdrawablecomposer_camera,Rdrawablecomposer_music,
      Rdrawablecomposer_sleep,Rdrawablecomposer_music,Rdrawablecomposer_place}; 

  public PathMenuView(Context context){
    super(context);
    setupViews();
  } 

  public PathMenuView(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = contextobtainStyledAttributes(attrs,
        RstyleablePathMenuView); 

    position = agetInt(RstyleablePathMenuView_position,3); 

    arecycle();
    setupViews();
  } 

  private void setupViews(){
    mContext = getContext(); 

    mHEIGHT = mContextgetResources()getDisplayMetrics()heightPixels;
    mWIDTH = mContextgetResources()getDisplayMetrics()widthPixels;
    mDensity = mContextgetResources()getDisplayMetrics()density; 

    xOffset = (int) (667 * mDensity);
    yOffset = (int) (667 * mDensity); 

    mHome = new ImageView(mContext); 

    mHomesetImageResource(Rdrawablecomposer_button);
    mHomesetOnClickListener(listener); 

    addView(mHome); 

    LayoutParams mHomeparams = (FrameLayoutLayoutParams)mHomegetLayoutParams();
    mHomeparamswidth = LayoutParamsWRAP_CONTENT;
    mHomeparamsheight = LayoutParamsWRAP_CONTENT; 

    switch (position) {
    case LEFT_TOP:
      mHomeparamsgravity = GravityLEFT | GravityTOP;
      for (int i = 0; i < menuResIdslength; i++) { 

        int width_padding = mWIDTH / ((menuResIdslength - 1) * 2);
        int height_padding = mHEIGHT / ((menuResIdslength - 1) * 2); 

        ImageView imageView = new ImageView(mContext);
        imageViewsetImageResource(menuResIds[i]);
        addView(imageView);
        LayoutParams params = (FrameLayoutLayoutParams) imageView
            getLayoutParams();
        paramswidth = LayoutParamsWRAP_CONTENT;
        paramsheight = LayoutParamsWRAP_CONTENT;
        paramsleftMargin = mWIDTH / 2
            - ((menuResIdslength - i - 1) * width_padding);
        paramstopMargin = mHEIGHT / 2 - i * height_padding;
        paramsgravity = GravityLEFT | GravityTOP;
        imageViewsetLayoutParams(params); 

      } 

      break;
    case RIGHT_TOP:
      mHomeparamsgravity = GravityRIGHT | GravityTOP;
      for (int i = 0; i < menuResIdslength; i++) { 

        int width_padding = mWIDTH / ((menuResIdslength - 1) * 2);
        int height_padding = mHEIGHT / ((menuResIdslength - 1) * 2); 

        ImageView imageView = new ImageView(mContext);
        imageViewsetImageResource(menuResIds[i]);
        addView(imageView);
        LayoutParams params = (FrameLayoutLayoutParams) imageView
            getLayoutParams();
        paramswidth = LayoutParamsWRAP_CONTENT;
        paramsheight = LayoutParamsWRAP_CONTENT;
        paramsrightMargin = mWIDTH / 2
            - ((menuResIdslength - i - 1) * width_padding);
        paramstopMargin = mHEIGHT / 2 - i * height_padding;
        paramsgravity = GravityRIGHT | GravityTOP;
        imageViewsetLayoutParams(params); 

      }
      break;
    case RIGHT_BOTTOM:
      mHomeparamsgravity = GravityRIGHT | GravityBOTTOM;
      for (int i = 0; i < menuResIdslength; i++) { 

        int width_padding = mWIDTH / ((menuResIdslength - 1) * 2);
        int height_padding = mHEIGHT / ((menuResIdslength - 1) * 2); 

        ImageView imageView = new ImageView(mContext);
        imageViewsetImageResource(menuResIds[i]);
        addView(imageView);
        LayoutParams params = (FrameLayoutLayoutParams) imageView
            getLayoutParams();
        paramswidth = LayoutParamsWRAP_CONTENT;
        paramsheight = LayoutParamsWRAP_CONTENT;
        paramsrightMargin = mWIDTH / 2
            - ((menuResIdslength - i - 1) * width_padding);
        paramsbottomMargin = mHEIGHT / 2 - i * height_padding;
        paramsgravity = GravityRIGHT | GravityBOTTOM;
        imageViewsetLayoutParams(params); 

      }
      break;
    case LEFT_BOTTOM:
      mHomeparamsgravity = GravityLEFT | GravityBOTTOM;
      for(int i = 0; i < menuResIdslength; i++){ 

        int width_padding = mWIDTH / ((menuResIdslength - 1) * 2);
        int height_padding = mHEIGHT / ((menuResIdslength -1) * 2); 

        ImageView imageView = new ImageView(mContext);
        imageViewsetImageResource(menuResIds[i]);
        addView(imageView);
        LayoutParams params = (FrameLayoutLayoutParams)imageViewgetLayoutParams();
        paramswidth = LayoutParamsWRAP_CONTENT;
        paramsheight = LayoutParamsWRAP_CONTENT;
        paramsleftMargin = mWIDTH / 2 - ((menuResIdslength - i - 1) * width_padding);
        paramsbottomMargin = mHEIGHT / 2 - i * height_padding;
        paramsgravity = GravityLEFT | GravityBOTTOM;
        imageViewsetLayoutParams(params);
      }
      break;
    default:
        break;
    }   

    mHomesetLayoutParams(mHomeparams);
  } 

  private OnClickListener listener = new OnClickListener() { 

    public void onClick(View v) {
      if (!bMenuShow) {
        startAnimationIn(PathMenuViewthis, 300);
      } else {
        startAnimationOut(PathMenuViewthis, 300);
      }
      bMenuShow = !bMenuShow;
    }
  }; 

  /**
   * 菜单隐藏动画
   *
   * @param group
   * @param duration
   */
  private void startAnimationIn(ViewGroup group, int duration) {
    for (int i = 1; i < groupgetChildCount(); i++) {
      ImageView imageview = (ImageView) groupgetChildAt(i);
      imageviewsetVisibility(0);
      MarginLayoutParams mlp = (MarginLayoutParams) imageview
          getLayoutParams(); 

      Animation animation = null; 

      switch (position) {
      case LEFT_TOP:
        animation = new TranslateAnimation(0F,-mlpleftMargin+xOffset,0F,-mlptopMargin + yOffset);
        break;
      case RIGHT_TOP:
        animation = new TranslateAnimation(mlprightMargin - xOffset,0F,-mlptopMargin + yOffset,0F);
        break;
      case LEFT_BOTTOM:
        animation = new TranslateAnimation(0F, -mlpleftMargin+ xOffset, 0F, -yOffset + mlpbottomMargin);
        break; 

      case RIGHT_BOTTOM:
        animation = new TranslateAnimation(mlprightMargin-xOffset,0F,-yOffset + mlpbottomMargin, 0F);
        break;
      default:
        break;
      } 

      animationsetFillAfter(true);
      animationsetDuration(duration);
      animationsetStartOffset((i * 100) / (-1 + groupgetChildCount()));
      animationsetInterpolator(new OvershootInterpolator(2F));
      imageviewstartAnimation(animation); 

    }
  } 

  /**
   * 菜单显示动画
   *
   * @param group
   * @param duration
   */
  private void startAnimationOut(ViewGroup group,int duration){
    for (int i = 1; i < groupgetChildCount(); i++) {
      final ImageView imageview = (ImageView) group
          getChildAt(i);
      MarginLayoutParams mlp = (MarginLayoutParams) imageviewgetLayoutParams(); 

      Animation animation = null; 

      switch (position) {
      case LEFT_TOP:
        animation = new TranslateAnimation(-mlpleftMargin+xOffset,0F,-mlptopMargin + yOffset,0F);
        break;
      case RIGHT_TOP:
        animation = new TranslateAnimation(0F,mlprightMargin - xOffset,0F,-mlptopMargin + yOffset);
        break; 

      case LEFT_BOTTOM:
        animation = new TranslateAnimation(-mlpleftMargin+xOffset,0F, -yOffset + mlpbottomMargin,0F);
        break; 

      case RIGHT_BOTTOM:
        animation = new TranslateAnimation(0F,mlprightMargin-xOffset, 0F,-yOffset + mlpbottomMargin);
        break;
      default:
        break;
      } 

      animationsetFillAfter(true);animationsetDuration(duration);
      animationsetStartOffset(((groupgetChildCount()-i) * 100)
          / (-1 + groupgetChildCount()));
      animationsetInterpolator(new AnticipateInterpolator(2F));
      imageviewstartAnimation(animation);
    }
  } 

} 

第四步:PathTestActivity.java以及用到的布局文件main.xml代码如下:

PathTestActivity.java(基本没修改代码)代码如下:

package comtutorpath; 

import androidappActivity;
import androidosBundle; 

public class PathTestActivity extends Activity { 

  @Override
  public void onCreate(Bundle savedInstanceState) {
    superonCreate(savedInstanceState);
    setContentView(Rlayoutmain);
  }
} 

main.xml代码如下:

<?xml version="0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemasandroidcom/apk/res/android"
  xmlns:tutor="http://schemasandroidcom/apk/res/comtutorpath"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" > 

  <comtutorpathPathMenuView
    android:id="@+id/text"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tutor:position="right_bottom"
     /> 

</LinearLayout> 

运行点击效果如下:

图1:默认是在右下方这里menuResIds定义了五个菜单


图2:点击红色菜单,菜单收回.

下面我们修改main.xml的tutor属性为left_bottom,并且修改PathMenuView.java中的menuResIds.

tutor:position="left_bottom" 

效果如下:

图3:自定义在左下角,六个菜单。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android 开发实例简单涂鸦板

    在Android上开发一些小应用既可以积累知识又可以增加乐趣,与任务式开发不同,所以想到在Android系统上实现一个简单的涂鸦板,这是我们练手的一种好的方法.   涂鸦板应用的代码实现 新建工程MyWall,修改/res/layout/main.xml文件,在里面添加一个SurfaceView和两个Button,用到了RelativeLayout布局,完整的main.xml文件如下: XML/HTML代码 <?xml version="1.0" encoding="u

  • Android 使用Path实现涂鸦功能

    今天实现一个涂鸦效果,会分几步实现,这里有一个重要的知识点就是图层,要理解这个,不然你看这篇博客,很迷茫,迷茫的苍茫的天涯是我的爱,先从简单的需求做起,绘制一条线,代码如下: package com.tuya; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.util

  • Android Path绘制贝塞尔曲线实现QQ拖拽泡泡

    这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下: 最终效果图 接下来一步一步的实现整个过程. 基本原理 其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的.然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小.最后就是松手后返回或者爆裂的实现. Path介绍: 顾名思义,就是一个路径的意思,Path里面有很多的方法,本次设计主要用到的相关方法有 moveTo() 移动Path到一个指定的点 qua

  • Android通过Path实现搜索按钮和时钟复杂效果

    在Android中复杂的图形的绘制绝大多数是通过path来实现,比如绘制一条曲线,然后让一个物体随着这个曲线运动,比如搜索按钮,比如一个简单时钟的实现: 那么什么是path呢! 定义:path  就是路径,就是图形的路径的集合,它里边包含了路径里边的坐标点,等等的属性.我们可以获取到任意点的坐标,正切值. 那么要获取Path上边所有点的坐标还需要用到一个类,PathMeasure; PathMesure: PathMeasure是一个用来测量Path的类,主要有以下方法: 构造方法 公共方法 可

  • Android编程开发之在Canvas中利用Path绘制基本图形(圆形,矩形,椭圆,三角形等)

    本文实例讲述了Android编程开发之在Canvas中利用Path绘制基本图形的方法.分享给大家供大家参考,具体如下: 在Android中绘制基本的集合图形,本程序就是自定义一个View组件,程序重写该View组件的onDraw(Canvase)方法,然后在该Canvas上绘制大量的基本的集合图形. 直接上代码: 1.自定义的View组件代码: package com.infy.configuration; import android.content.Context; import andro

  • Android编程实现在Bitmap上涂鸦效果

    本文实例讲述了Android编程实现在Bitmap上涂鸦效果.分享给大家供大家参考,具体如下: 布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" andro

  • Android自定义View系列之Path绘制仿支付宝支付成功动画

    前言 使用支付宝付款时,我们可以看到成功或者失败都会有个动画提示,如果我们需要做这样的效果的话,当然,你可以让设计师给你做个GIF,但是我们知道图像比较耗内存的,我们自己可以用代码实现还是代码实现好点吧. 效果 实现方法 首先我们需要了解PathMeasure这个类,这个类我们可以理解为用来管理Path.我们主要看几个方法. PathMeasure(): 构造方法 ,实例化一个对象 PathMeasure(Path path,boolean isClosed):传入Path对象和是否闭合,pat

  • Android高手进阶教程(二十六)之---Android超仿Path菜单的功能实现!

    Hi~大家好,出来创业快3个月了,一切还不错,前一段时间用了业余时间搞了个问答类网站YQMA.想做中国的stackoverflow,哈哈,只是YY下,希望大家多多支持! 好了,今天给大家分享的是Path菜单的简单实现,可以支持自定义方向(左上,右上,右下,左下),并且可以自定义菜单的个数,难点就是菜单的摆放位置(动态设置margin),还有动画的实现,其实动画只是简单用了个TranslateAnimation,N个菜单一起移动的时候感觉很cool~ 这里也用到了自定义标签,这里不懂的童鞋可以看我

  • Android高手进阶教程(二十二)之Android中几种图像特效处理的集锦汇总!!

    大家好,这一节给大家分享的是Android中几种图像特效处理的小技巧,比如圆角,倒影,还有就是图片缩放,Drawable转化为Bitmap,Bitmap转化为Drawable等等. 废话少说了,直接讲解今天的实例,本例主要是先获取壁纸(getWallpaper()),然后对当前壁纸的一些特效处理.大家按步骤一步一步来: 第一步:新建一个Android工程命名为ImageDemo,工程结构如下: 第二步:新建一个.Java文件,命名为ImageUtil.java,在里面定义一些图片处理方法,代码如

  • Python入门教程(二十九)Python的RegEx正则表达式

    RegEx 或正则表达式是形成搜索模式的字符序列. RegEx 可用于检查字符串是否包含指定的搜索模式. RegEx 模块 Python 提供名为 re 的内置包,可用于处理正则表达式. 导入 re 模块: import re Python 中的 RegEx 导入 re 模块后,就可以开始使用正则表达式了: 实例 检索字符串以查看它是否以 “China” 开头并以 “country” 结尾: import re txt = "China is a great country" x =

  • Python入门教程(二十八)Python中的JSON

    JSON 是用于存储和交换数据的语法. JSON 是用 JavaScript 对象表示法(JavaScript object notation)编写的文本. Python 中的 JSON Python 有一个名为 json 的内置包,可用于处理 JSON 数据. 实例 导入 json 模块: import json 解析 JSON - 把 JSON 转换为 Python 若有 JSON 字符串,则可以使用 json.loads() 方法对其进行解析. 结果将是 Python 字典 实例 把 JS

  • Python入门教程(二十五)Python的作用域

    目录 局部作用域 函数内部的函数 全局作用域 命名变量 Global 关键字 变量仅在创建区域内可用.这称为作用域. 局部作用域 在函数内部创建的变量属于该函数的局部作用域,并且只能在该函数内部使用. 实例 在函数内部创建的变量在该函数内部可用: def myfunc(): x = 100 print(x) myfunc() 运行实例 100 函数内部的函数 如上例中所示,变量 x 在函数外部不可用,但对于函数内部的任何函数均可用: 实例 能够从函数内的一个函数访问局部变量: def myfun

  • Python入门教程(二十二)Python的类和对象

    目录 Python 类/对象 创建类 创建对象 init() 函数 对象方法 self 参数 修改对象属性 删除对象属性 删除对象 pass 语句 Python 类/对象 Python 是一种面向对象的编程语言. Python 中的几乎所有东西都是对象,拥有属性和方法. 类(Class)类似对象构造函数,或者是用于创建对象的“蓝图”. 创建类 如需创建类,请使用 class 关键字: 实例 使用名为 x 的属性,创建一个名为 MyClass 的类: class MyClass: x = 5 运行

  • Python入门教程(二十)Python的Lambda表达式

    目录 语法 lambda 函数可接受任意数量的参数: 为何使用 Lambda 函数? lambda 函数是一种小的匿名函数. lambda 函数可接受任意数量的参数,但只能有一个表达式. 语法 lambda arguments : expression 执行表达式并返回结果: 实例 一个 lambda 函数,它把作为参数传入的数字加 10,然后打印结果: x = lambda a : a + 10 print(x(5)) 运行实例 17 lambda 函数可接受任意数量的参数: 实例 一个 la

  • Android项目实战(二十八):使用Zxing实现二维码及优化实例

    前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中我们也许只会用到二维码的扫描和生成两个功能,所以不必下载完整的jar包,使用简化版的即可,下文可见. 这篇文章讲述: 1.如果快速在项目中集成zxing,实现扫描和生成二维码功能 2.根据项目需求去修改源码实现我们的要求并进行优化 一.快速集成zxing二维码 1.下载库文件 :http://xiazai.jb51.net/201611/yuanma/ZXingB

  • PostgreSQL教程(二十):PL/pgSQL过程语言

    一.概述: PL/pgSQL函数在第一次被调用时,其函数内的源代码(文本)将被解析为二进制指令树,但是函数内的表达式和SQL命令只有在首次用到它们的时候,PL/pgSQL解释器才会为其创建一个准备好的执行规划,随后对该表达式或SQL命令的访问都将使用该规划.如果在一个条件语句中,有部分SQL命令或表达式没有被用到,那么PL/pgSQL解释器在本次调用中将不会为其准备执行规划,这样的好处是可以有效地减少为PL/pgSQL函数里的语句生成分析和执行规划的总时间,然而缺点是某些表达式或SQL命令中的错

  • 在ASP.NET 2.0中操作数据之二十六:排序自定义分页数据

    导言 和默认翻页方式相比,自定义分页能提高几个数量级的效率.当我们的需要对大量数据分页的时候就需要考虑自定义分页,然而实现自定义分页相比默认分页需要做更多工作.对于排序自定义分页数据也是这样,在本教程中我们就会扩展前面的例子来实现自定义分页数据的排序. 注意:既然本教程是基于前一个的,因此我们需要把前面教程示例页面EfficientPaging.aspx的<asp:Content>元素中的代码复制到本教程SortParameter.aspx示例页面中.关于如何进行这样的复制操作请参看为删除数据

随机推荐