Android实现自定义左右菜单

功能描述:
在左中右三个区域分别承载三个不同的view,把它全部添加进来,而我们实现左右菜单,就是来控制当前显示的是哪一部分;如果显示中间菜单,就把中间菜单呈现在用户面前;如果显示左菜单,就把左菜单和一部分的中间菜单呈现在用户面前;如果显示右菜单,就把右菜单和一部分的中间菜单呈现在用户面前。即整张图的左中右不断移动。

功能实现(step by step):
1、菜单布局(通过扩展,完成基本布局。包含左右菜单布局和中间内容区域,并设置颜色用于区分)2、菜单左右滑动(使用逻辑,找出滑动中间点,并对事件作处理,主要针对滑动事件)3、加入左右滑动动画(使用Scroll进行滑动事件处理,主要是自动滑动,增加用户体验)4、处理点击事件(通过对左右事件的处理,会阻止点击事件的处理,这里需要调用系统,返还点击事件)5、添加蒙板效果(通过增加view,对view的透明度进行处理,从而实现蒙版效果)

核心内容:
1.布局的添加
2.事件分发机制
3.滚动添加
4.蒙版添加

构想图:

开发过程中遇到的错误:
Caused by:java.lang.IllegalStateException:The specified child already has a parent.You must call removeView() on the child’s parent first.
造成这个原因,是组件在父类中重复加载了相同的组件
如:
addView(leftMenu, mLayout); // 第一次添加
addView((leftMenu, , mLayout); // 第二次添加

源代码:

activity_main.xml:

<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= ".MainActivity" ></RelativeLayout>

left.xml:

<?xml version= "1.0" encoding ="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width= "match_parent"android:layout_height= "match_parent"android:orientation= "vertical" ><Button
        android:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button" /></LinearLayout>

MainActivity.java

package com.example.mymenu;import android.os.Bundle;
import android.app.Activity;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;public class MainActivity extends FragmentActivity {private MainUI mainUI ;private LeftMenu leftMenu ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//实例化MainUImainUI= new MainUI(this );setContentView( mainUI);//实例化LeftMenuleftMenu= new LeftMenu();getSupportFragmentManager().beginTransaction().add(MainUI. LEFT_ID,leftMenu ).commit();}}

MainUI.java

package com.example.mymenu;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;

@SuppressLint(“NewApi” )
public class MainUI extends RelativeLayout{

    private Context context ;private FrameLayout leftMenu ;private FrameLayout middleMenu ;private FrameLayout rightMenu ;//设置蒙版private FrameLayout middleMask ;private Scroller mScroller ;//定义左中右菜单的IDpublic static final int LEFT_ID=0xaabbcc;public static final int MIDDLE_ID=0xaaccbb;public static final int RIGHT_ID=0xccbbaa;//继承自RelativeLayout,并实现两个构造方法public MainUI(Context context) {super(context);initView(context);}public MainUI(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}public void initView(Context context){this.context =context;mScroller=new Scroller(context, new DecelerateInterpolator()); //渲染器//实例化三个区域leftMenu= new FrameLayout(context);middleMenu=new FrameLayout(context);rightMenu=new FrameLayout(context);middleMask=new FrameLayout(context);//分别为三个区域添加不同的颜色leftMenu.setBackgroundColor(Color. RED);middleMenu.setBackgroundColor(Color.GREEN);rightMenu.setBackgroundColor(Color.RED);middleMask.setBackgroundColor(0x88000000);//蒙版的初始颜色为浅灰色//为自定义的左中右菜单添加IDleftMenu.setId( LEFT_ID);middleMenu.setId(MIDDLE_ID );rightMenu.setId(RIGHT_ID );        //把三个区域全部填充到一个view中,实际上这个view就是我们承载三个区域的最外层的RelativeLayout,也是当前的主文件addView( leftMenu);addView( middleMenu);addView( rightMenu);addView( middleMask);middleMask.setAlpha(0);//设置蒙版的科技度(完全可见),只有在滑动的时候才是不可见的}public float onMiddleMask(){System. out.println("透明度:" +middleMask .getAlpha());return middleMask .getAlpha();     }//根据滑动的距离的变化来设置可见度值的变化@Overridepublic void scrollTo(int x, int y) {super.scrollTo(x, y);onMiddleMask();int curX=Math.abs(getScrollX());float scale=curX/(float)leftMenu.getMeasuredWidth(); //计算整个可见宽度middleMask.setAlpha(scale);}//在把三个区域添加到Layout之前,先对它们的宽和高进行测量@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//widthMeasureSpec,heightMeasureSpec是当前屏幕的宽度和高度super.onMeasure(widthMeasureSpec, heightMeasureSpec);//中间菜单的宽和高即为当前屏幕的宽和高middleMenu.measure(widthMeasureSpec, heightMeasureSpec);//获取蒙版的宽和高middleMask.measure(widthMeasureSpec, heightMeasureSpec);//得到当前屏幕的真实宽度int realWidth=MeasureSpec.getSize(widthMeasureSpec);//计算所需要的宽度int tempWidthMeasure=MeasureSpec.makeMeasureSpec(( int)(realWidth*0.8f),MeasureSpec.EXACTLY);//为左右菜单设置相应的宽度leftMenu.measure(tempWidthMeasure, heightMeasureSpec);rightMenu.measure(tempWidthMeasure, heightMeasureSpec);       }//把三个区域添加到Layout中@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {//五个参数(改变监听,left,top,right,bottom)super.onLayout(changed, l, t, r, b);//设置三个区域在layout中的位置middleMenu.layout(l, t, r, b);middleMask.layout(l, t, r, b);leftMenu.layout(l- leftMenu.getMeasuredWidth(), t, l, b);//(l-leftMenu.getMeasuredWidth(), t, r, b)rightMenu.layout(r,t,r+ rightMenu.getMeasuredWidth(),b); //(l+middleMenu.getMeasuredWidth(), t, l+middleMenu.getMeasuredWidth()+rightMenu.getMeasuredWidth(), b)}private boolean isTestCompete ;private boolean isleftrightEvent ;//标识上下滑动和左右滑动,左右滑动为true,上下滑动为false//事件分发@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {//检测到一个事件,判断是什么事件(上下滑动事件、左右滑动事件、点击事件)if (!isTestCompete ) {//没有初始化,默认为falsegetEventType(ev); //创建一个判断处理事件的方法return true ;}if (isleftrightEvent ) {//左右滑动应该触发的响应switch (ev.getActionMasked()) {case MotionEvent.ACTION_MOVE://你手指滑动的距离即为当前屏幕滑动的距离int curScrollX=getScrollX();//定义屏幕滑动的距离int dis_x=(int )(ev.getX()-point.x);//定义手指滑动的距离int expectX=-dis_x+curScrollX;//根据差值判断是向左滑动还是向右滑动(差值大于20是向右滑动;差值小于20是向左滑动)int finalX=0; //定义屏幕最终的左右滑动距离if (expectX<0) {//向左finalX=Math. max(expectX, -leftMenu.getMeasuredWidth());//最大值} else{ //向右finalX=Math. min(expectX, rightMenu.getMeasuredWidth());//最小值}scrollTo(finalX, 0); //使屏幕移动到最终的位置point. x=( int)ev.getX();break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:curScrollX=getScrollX();if (Math.abs(curScrollX)> leftMenu.getMeasuredWidth() >> 1) {//当屏幕滑动的距离大于左右菜单的一半时,自动跳到相应的菜单if (curScrollX<0) {mScroller.startScroll(curScrollX,0,- leftMenu.getMeasuredWidth()-curScrollX, 0,200); //起始X,起始Y,终止X,终止Y} else {mScroller.startScroll(curScrollX,0,leftMenu.getMeasuredWidth()-curScrollX, 0,200);}} else {//返回原点mScroller.startScroll(curScrollX,0,-curScrollX, 0,200);}invalidate(); //调用view的重绘方法进行刷新isleftrightEvent=false ;isTestCompete=false ;                     break;}} else{//对上下滑动和点击触发的响应switch (ev.getActionMasked()) {case MotionEvent.ACTION_UP:isleftrightEvent=false ;isTestCompete=false ;break;default:             break;}}return super .dispatchTouchEvent(ev);}//重写回调方法,使左右菜单进行滑动@Overridepublic void computeScroll() {super.computeScroll();if (mScroller .computeScrollOffset()) {return;}int tempX=mScroller .getCurrX();scrollTo(tempX, 0);}private Point point =new Point(); //获取当前屏幕的点,通过点来计算滑动的距离,根据滑动的距离来判断相应的事件是滑动还是点击private static final int TEST_DIS=20; //如果移动距离大于20就定义为滑动//判断处理事件private void getEventType(MotionEvent ev) {switch (ev.getActionMasked()) {case MotionEvent.ACTION_DOWN:point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();super.dispatchTouchEvent(ev);break;case MotionEvent.ACTION_MOVE:int dX= Math.abs(( int)ev.getX()-point .x );//获取水平方向上移动距离的绝对值int dY=Math.abs(( int)ev.getY()-point .y );//获取垂直方向上移动距离的绝对值if (dX>=TEST_DIS && dX>dY) {//左右滑动isleftrightEvent=true ;isTestCompete=true ; //允许进入接下来的返出测试阶段//获取当前点的 xy坐标point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();} else if (dY>=TEST_DIS && dY>dX) { //上下滑动isleftrightEvent=false ;isTestCompete=true ;//允许进入接下来的返出测试阶段point. x=( int) ev.getX();//floatpoint. y=( int) ev.getY();}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL://触摸到屏幕的边缘//对点击事件进行处理(返还给系统处理)super.dispatchTouchEvent(ev);isleftrightEvent=false ;isTestCompete=false ;break;}}

}

LeftMeau.java:

package com.example.mymenu;import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;public class LeftMenu extends Fragment{@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View v=inflater.inflate(R.layout. left, container,false);//引入left.xml布局文件//为button按钮添加监听事件v.findViewById(R.id. button1).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {System. out.println("Hello LMB!!" );                     }});return v;}
}

Android项目开发实战—自定义左右菜单相关推荐

  1. Android项目开发实战常用知识点

    Android项目开发实战常用知识点 一:启动页延时两秒再跳转到主界面: //执行类 private class SlpashRunnable implements Runnable {@Overri ...

  2. android2048项目报告,Android项目开发实战-2048游戏

    <2048>是一款比较流行的数字游戏,最早于2014年3月20日发行.原版2048首先在GitHub上发布,原作者是Gabriele Cirulli,后被移植到各个平台.这款游戏是基于&l ...

  3. Android阿面试积累,android项目开发实战密码

    公差为1的等差数列求和,如何优化 自己的优势在哪里 注解如何获取,反射为何耗性能? Java的GC机制,分代回收策略 Binder机制:ServiceManager什么时候注册的? int,long的 ...

  4. Android项目开发实战之绘图

    视频课:[免费]跨平台APP JQuery Mobile开发-1-初探移动开发-张晨光的在线视频教程-CSDN程序员研修院 学习内容 Ø Android中基本图形的绘制 Ø Android文本的绘制 ...

  5. Android项目开发实战—倒计时[Handler,Timer,TimerTask,Message]

    Android实现倒计时 先上一个本人实际操作中的界面: 源代码: (activity_main.xml和MainActivity.java) activity_main.xml: <Linea ...

  6. android趣味项目,AndroidStudio项目开发实战——从基础入门到趣味开发

    AndroidStudio项目开发实战--从基础入门到趣味开发 编辑 锁定 讨论 上传视频 <AndroidStudio项目开发实战--从基础入门到趣味开发>是2020年3月电子工业出版社 ...

  7. android网络游戏开发实战pdf_Python项目开发实战+第2版PDF高清文档下载

    本书来自真正的开发现场,是BePROUD公司众多极客在真实项目中的经验总结和智慧结晶.作者从Python的环境搭建开始讲起,介绍了Web应用的开发方法.项目管理及审查.测试与高效部署.服务器调试等内容 ...

  8. 《Android Studio开发实战 从零基础到App上线》第一版的资源下载和内容勘误

    资源下载 下面是<Android Studio开发实战 从零基础到App上线>(第一版)一书用到的工具和代码资源: 1.本书使用的Android Studio版本为2.2.3,因为Andr ...

  9. 《Android Studio开发实战 从零基础到App上线》源码运行问题解答

    本书提供了所有章节的完整源码下载,自上市以来陆续收到读者的意见反馈,现将与源码有关的问题汇总归类如下,方便更多的朋友解决源码运行过程中发现的问题: 一.打开本书源码时,出现"Plugin w ...

最新文章

  1. linux系统硬盘坏道,如何在 Linux 系统下检测硬盘上的坏道和坏块
  2. python 删除文件、目录_python实现删除文件与目录的方法
  3. code1928: 日期差值 技巧模拟
  4. cv2.dnn.readNetFromDarknet()在python3上遇到的问题
  5. Java SE 第二十三讲----static关键字and final关键字
  6. SWPUACM第一届程序设计大赛
  7. linux ping 不通 arp incomplete,关于show arp表,显示Incomplete问题
  8. 如何进行业务需求分析
  9. 老生常谈!数据库如何存储时间?你真的知道吗?
  10. VS2017 和 Matlab R2016b 混合编程配置问题解决!
  11. 今日头条文章量如何打造爆款
  12. Perl之Spreadsheet::WriteExcel安装
  13. 多可文档管理软件权限说明(4)——权限累加规则
  14. ioDraw - 免费的在线图表制作工具
  15. 以太网之父鲍勃·梅特卡夫获2022图灵奖,76岁进入新领域再出发!
  16. php 命格算法,命格是怎么个算法???
  17. 开源好物分享!文档在线预览平台
  18. 人生观、价值观、世界观
  19. html页面中加skype,分享个刚学会的电子邮件中加Skype即时联络标签的方法~有用请顶...
  20. 运营商级NAT(Carrier-grade NAT)

热门文章

  1. 人类大脑每日24小时工作节奏表
  2. android jni new/delete 和 new[]/delete[]
  3. android 打包jar包
  4. android hardware architecture
  5. define 解析依赖,判断状态,初始化/触发加载 --------require 同步加载(直接返回)/异步加载(创建匿名模块,判断状态,初始化/触发加载)
  6. jdbc连接Oracle/MySQL数据库进行批量导入操作,如何提高效率???
  7. Linux中默认的JDK版本设置
  8. java-01 JAVA三大版本比较JDK、JRE、JVM 的关系
  9. SpringSecurity的简单使用使用案列说明
  10. winrar5.50去广告教程(仅供学习使用)