自定义控件_测滑菜单栏

自定义控件–侧滑菜单栏

这里写图片描述
这里写图片描述

这里用到的知识-事件分发机制:

 dispatchTouchEvent

> 用于控制事件是否往下下发

> 分发事件

     * super : 事件会接着往下走

     * true | false: 事件不会往下下发了,到此结束。

* onInterceptTouchEvent

> 用于控制事件是否会传递给孩子

> 拦截事件

     * super | false: 不拦截, 事件会传递给孩子
     * true : 拦截事件,不会传递给孩子,自己处理。

* onTouchEvent

> 事件是否处理,如果不处理,会交给父容器去处理

> 处理事件

         * super | false  : 事件还会传给父容器
         * true : 事件我们自己处理,不要给别人了。

> dispatchTouchEvent -- onInterceptTouchEvent  -- onTouchEvent

这里写图片描述

布局部分

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" >
<com.lxk.slidemenu.view.SlideMenuView
    android:id="@+id/sv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
     <include layout="@layout/view_content"/>
     <include layout="@layout/view_slidemenu"/>
</com.lxk.slidemenu.view.SlideMenuView>
</RelativeLayout>

view_content.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" >
<LinearLayout 
    android:background="@drawable/top_bar_bg"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"

    <ImageView
        android:id="@+id/iv_sm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/main_back"
        />
    <ImageView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/top_bar_divider"
        />
    <TextView 
        android:layout_gravity="center_vertical"
        android:textColor="#FFFFFF"
        android:textSize="20sp"
        android:text="新闻中心"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="内容区域"
    android:textSize="20sp" />
</RelativeLayout>
</LinearLayout>

view_slidemenu.xml 测滑菜单内容

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<ScrollView
    android:background="@drawable/menu_bg"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <LinearLayout
        android:orientation="vertical"
        android:padding="15dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_news"
            android:text="新闻" />
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_read"
            android:text="订阅" />
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_local"
            android:text="本地" />
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_ties"
            android:text="跟帖" />
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_pics"
            android:text="图片" />
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_ugc"
            android:text="话题" />
        <TextView
            style="@style/Tar_Style"
            android:drawableLeft="@drawable/tab_vote"
            android:text="投票" />
        <TextView
            style="@style/Tar_Style"
            android:text="聚合读物" />
    </LinearLayout>
    </ScrollView>
</LinearLayout>

代码部分

MainActivity

package com.lxk.slidemenu;
import com.lxk.slidemenu.view.SlideMenuView;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class MainActivity extends Activity implements OnClickListener {

private SlideMenuView mSv;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
}
//初始化控件
private void initView() {
    ImageView sm=(ImageView) findViewById(R.id.iv_sm);
    mSv = (SlideMenuView) findViewById(R.id.sv);
    sm.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    //打开或关闭侧栏菜单
    mSv.setShow();

}


}

SlideMenuView

package com.lxk.slidemenu.view;

import android.R.id;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
/**
 * 自定义绘画三步骤
 *  1.测量--测量控件的大小
 *  2.布局--设置控件的位置
 *  3.绘画--
 *
 */
public class SlideMenuView extends ViewGroup{

private static  boolean isShow = false;
private Scroller mScroller;
private View mContentView;
private View mSlideView;



public SlideMenuView(Context context, AttributeSet attrs) {
        super(context, attrs);
    //负责处理
    mScroller = new Scroller(context);  

    }
//测量控件的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //设置孩子的控件的大小--这里使用自己在布局的大小的
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    //设置自己的控件的大小
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}


//设置控件的位置
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    //设置孩子的位置这里一共有两个孩子--一个是contentview,一个是slidemenuview
    mContentView = getChildAt(0);//内容菜单
    mSlideView = getChildAt(1);//测滑菜单
    //设置内容菜单的位置(一开始显示的内容菜单)--一个View的位置一般由它的坐上角坐标(left,top)和它的右下角(right,bottom)坐标决定的
    //
    int contentLeft=0;  //左上角的X坐标
    int contentTop=0;   //左上角的Y坐标
    int contentRight=mContentView.getMeasuredWidth();    //右下角的X坐标--getMeasuredWidth()方法是获取该控件的长度,getWidth()是布局设置完成后可以设置了
    int contentBotoom=mContentView.getMeasuredHeight();//右下角的Y坐标
    mContentView.layout(contentLeft, contentTop, contentRight, contentBotoom);
    //设置测滑菜单的位置
    int slideLeft=-mSlideView.getMeasuredWidth(); //左上角的X坐标
    int slideTop=0;   //左上角的Y坐标
    int slideRight=0;    //右下角的X坐标--getMeasuredWidth()方法是获取该控件的长度,getWidth()是布局设置完成后可以设置了
    int slideBotoom=mSlideView.getMeasuredHeight();//右下角的Y坐标
    mSlideView.layout(slideLeft, slideTop, slideRight, slideBotoom);
}
//绘画--这里不需要绘画
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
}
//创建按下的坐标,移动的坐标,抬起的坐标
float mDwonX,mMoveX,mUpX;
//因为是要触摸滑动所要复写View控件的一个方法
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        //鼠标按下的时候的X坐标---因为滑动是针对左右移动所以不考虑Y坐标
        mDwonX=event.getX();
        //消除之前没完成的动作防止下一次按的时候之前动作还么完成
        mScroller.forceFinished(true);
        break;
    case MotionEvent.ACTION_MOVE:
        //鼠标移到到某个位置的时候的X坐标
        mMoveX=event.getX();
        //处理移动的事件处理
        performMove();

        break;
    case MotionEvent.ACTION_UP:
        mUpX=event.getX();//抬起时的X的坐标
        //鼠标抬起的时候处理事件
        performUp();
        break;

    default:
        break;
    }
    return true;//这里是返回true相当是只要发生触摸--事件都由我们自己处理不需要别人false是交给系统处理
}

//鼠标抬起时响应的事件
private void performUp() {
    int minddle=mSlideView.getMeasuredWidth()/2;
    //鼠标抬起时如果向右滑动距离小于菜单栏的一般就直接退回原点
    if (-getScrollX()<minddle) {
        System.out.println("少一一半= "+getScrollX());
        closeMenu();//关闭测滑菜单
    } else { //当向右滑动大于的时候就直接拖到右边边界点



        openMenu();//打开侧栏菜单
    }

}
public void openMenu() {
    int dx=mSlideView.getWidth()+getScrollX();//移动剩下的距离刚好把侧栏菜单一出来
    //设置移动需要的时间--也可以直接具体数值但是因为为了达到效果所以这里就动态的给出
    int duration=dx*8;
    if (duration>1000) {//如果duration大于一秒则就拿一秒来算,时间太长了不行
        duration=1000;
    }

    /*
     * Scroller类是移动动画效果;参数一:是开始移动的X坐标;参数二:开始移动的Y坐标
     * 参数三:水平移动距离,参数四:垂直移动的距离,参数五:总共持续的时间
     */
    mScroller.startScroll(getScrollX(), 0,-dx, 0, duration);
    //注意需要重新绘制一下
    invalidate();
    System.out.println("超一一半= "+getScrollX());
}

//关闭菜单
public void closeMenu() {
    int startX=getScrollX();//从当前的距离做为X坐标
    int startY=0;
    int dx=-getScrollX();//向右移动了多少距离,向相反向左移动多少距离回去,负数表示向右移动,正数表示想做移动
    int dy=0;//垂直方向不移动
    //设置移动需要的时间--也可以直接具体数值但是因为为了达到效果所以这里就动态的给出
    int duration=dx*8;
    if (duration>1000) {//如果duration大于一秒则就拿一秒来算,时间太长了不行
        duration=1000;
    }

    /*
     * Scroller类是移动动画效果;参数一:是开始移动的X坐标;参数二:开始移动的Y坐标
     * 参数三:水平移动距离,参数四:垂直移动的距离,参数五:总共持续的时间
     */
    mScroller.startScroll(startX, startY, dx, dy, duration);
    //注意需要重新绘制一下
    invalidate();
}

//这个是配置mScroller来完成的事件
@Override
public void computeScroll() {
    if (mScroller.computeScrollOffset()) {//如果当前的每移动完则继续移动完
        int currX = mScroller.getCurrX();
        scrollTo(currX, 0);
        //注意需要重新绘制一下
        invalidate();
    }

}

//处理移动的事件
private void performMove() {
    /*
     * scrollTo(x,y)--x为负数就是把左边不可见的x坐标内容拖出来显示,x为正数的时候,
     * 把右边不可见的x左边的内容拖出来显示
     */
    int dx=Math.round(mDwonX-mMoveX);//计算移动的距离
    /*
     * 首先判断一下左边的边界的问题--当鼠标往右边滑动的时候,左边拖出来的最大的长度应该是测滑菜单栏的长度.
     */
    //由于鼠标只要移动一点距离而且为正数,无法判断范围
    int finalX=dx+getScrollX();//
    if (finalX<-mSlideView.getMeasuredWidth()) {//当往右边的移动,计算dx为负数,getScrollX()为负数相加得负数
        dx=-mSlideView.getMeasuredWidth();
        scrollTo(dx, 0);
    } else if(finalX>0){//往左边移动的时候,最多把测滑菜单栏刚好全部推到出去的时候移动的距离  
        dx=0;
        scrollTo(dx, 0);
    }else{//如果是在中间的范围则使用srollBy(x,y);来滑动,这个方法是根据当前的坐标进行叠加移动,所以每移动一次后就要把当前的坐标赋值给作为按下的坐标
        scrollBy(dx, 0);
        mDwonX=mMoveX;
    }


}

//对外暴露一个方法,给MianActivity来点击按钮来调用显示还是隐藏测滑菜单
public void setShow(){

    if (isShow) {//如果现在正开则隐藏测滑菜单栏
        isShow=false;
        closeMenu();
    } else {//如果现在正开则显示测滑菜单栏
        isShow=true;
        openMenu();

    }
}

/*
 * 事件的传递机制三步骤:dispatchTouchEvent用于控件是否往下传递,2,onInterceptTouchEvent:用于控制事件是否会传递给孩子,3、onTouchEvent:事件是否处理,如果不处理,会交给父容器去处理
 */

//dispatchTouchEvent用于控件是否往下分发:分发事件:super接着往下走,true|false事件不会往下发生到此结束
//用于控制事件是否会传递给孩子

//拦截:* super | false: 不拦截, 事件会传递给孩子
// * true : 拦截事件,不会传递给孩子,自己处理。

float mDownY,mMoveY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    //在测滑菜单中如果是水平滑动我们需要移动拦截,如果是垂直移动事件往下发
    switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
        mDwonX=ev.getX();
        mDownY=ev.getY();
        break;
    case MotionEvent.ACTION_MOVE:
        mMoveX=ev.getX();
        mMoveY=ev.getY();
        //如果水平移动的距离大于垂直的则返回true
        int dx=Math.round(mMoveX-mDwonX);
        int dy=Math.round(mMoveY-mDownY);
        if (dx>dy) {
            System.out.println("水平移动--");
            return true;
        }
        break;
    case MotionEvent.ACTION_UP:

        break;
    default:
        break;
    }


    return super.onInterceptTouchEvent(ev);
}




}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值