欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义控件案例汇总2(自定义开关、下拉刷新、侧滑菜单)

程序员文章站 2024-02-26 21:36:22
案例四 自定义开关: 功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景。当滑动开关时,开关的滑块可跟随手指移动。当手指松开后,滑块根据开关的状...

案例四 自定义开关:

功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景。当滑动开关时,开关的滑块可跟随手指移动。当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保存开关的状态,将开关的状态回调给调用者。当然,上述功能系统给定的switch控件也可以实现。

实现步骤:

        1. 写一个类继承view,重写两个参数的构造方法。在构造方法中指定工作空间,通过attrs.getattributeresourcevalue方法将java代码中的属性值和xml中的属性值联系起来。这样可以在xml文件中指定相关的属性值。重写onmeasure和ondraw方法,绘制图片。这里测量图片大小直接用setmeasureddimension方法,获取图片本身的大小。
        2. 设置接口回调。对于图片来说,我们希望能够在调用者获取开关的状态,因此需要设置一个接口回调,用于监控开关的状态,当开关的状态发生变化时间调用。接口回调的优势在于调用者并不知道何时调用,所以在另一个文件中设置一个接口,在该文件触发事件。由于重写了接口的方法,因此,执行重写后的方法。这样就可以实现数据的回调。自定义控件中接口回调的应用较为广泛,几乎所有的控件都需要设置监听,且写法较为固定。
        3. 重写ontouchevent()方法。分析得知,开关由两部分组成,一部分是底座儿,一部分是划片而。当手指滑动时,划片儿应该跟随手指移动。当划片的左边小于底座左边坐标时,让划片左边的坐标和底座对齐,当划片的右边大于底座右边坐标时,让划片右边的坐标和底座对齐,这样保证划片不越界。当手指松开后,判断划片的中线坐标是在底座儿中线坐标的左边还是右边,以此来决定划片最终是停在左边还是右边。同时改变开关的状态,将开关的状态回调给控件的调用中,读取开关的状态。

代码实现。 主程序(调用者)中的代码:

package com.example.aswitch;

import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.widget.toast;

public class mainactivity extends appcompatactivity {

  private mytogglebutton togglebutton;

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    togglebutton = (mytogglebutton) findviewbyid(r.id.toggle_button);
    togglebutton.setonstatechangedlistener(new mytogglebutton.onstatechangedlistener() {
      @override
      public void onstatechanged(boolean state) {
        toast.maketext(mainactivity.this, state ? "开" : "关", toast.length_short).show();
      }
    });
  }
}


自定义开关的具体实现;

package com.example.aswitch;

import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.canvas;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;

/**
 * created by huang on 2016/12/1.
 */
public class mytogglebutton extends view {
  private bitmap background;
  private bitmap slideicon;
  private boolean state;
  private onstatechangedlistener monstatechangedlistener;
  private int backgroundwidth;
  private int backgroundheight;
  private int slideiconwidth;
  private int slideiconheight;
  private int slideiconleft;
  private int maxslideiconleft;

  public mytogglebutton(context context, attributeset attrs) {
    super(context, attrs);
    string namespace = "http://schemas.android.com/apk/res-auto";
    int slidebackgroundresid = attrs.getattributeresourcevalue(namespace, "slidebackground", -1);
    int slideiconresid = attrs.getattributeresourcevalue(namespace, "slideicon", -1);
    if (slidebackgroundresid != -1 && slideiconresid != -1) {
      setswitchimage(slidebackgroundresid, slideiconresid);
    }

    boolean state = attrs.getattributebooleanvalue(namespace, "state", false);
    setstate(state);
  }

  /**
   * 设置开关的图片
   * @param slidebackgroundresid 开关的背景图片资源id
   * @param slideiconresid 开关上面的滑块icon
   */
  public void setswitchimage(int slidebackgroundresid, int slideiconresid) {
    background = bitmapfactory.decoderesource(getresources(), slidebackgroundresid);
    slideicon = bitmapfactory.decoderesource(getresources(), slideiconresid);

    backgroundwidth = background.getwidth();
    backgroundheight = background.getheight();
    slideiconwidth = slideicon.getwidth();
    slideiconheight = slideicon.getheight();

    maxslideiconleft = backgroundwidth - slideiconwidth;
  }

  /** 设置开关按钮的状态 */
  public void setstate(boolean state) {
    checkstate(state);
    if (state) {
      slideiconleft = maxslideiconleft;
    } else {
      slideiconleft = 0;
    }
  }

  public void setonstatechangedlistener(onstatechangedlistener monstatechangedlistener) {
    this.monstatechangedlistener = monstatechangedlistener;
  }

  /** 开关按钮状态改变的监听器 */
  public interface onstatechangedlistener {
    void onstatechanged(boolean state);
  }

  /**
   * 对view进行测量的方法
   */
  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    setmeasureddimension(backgroundwidth, backgroundheight);
  }

  /** 把view画出来的方法
   * @param canvas 画布
   * */
  @override
  protected void ondraw(canvas canvas) {
    int left = 0;
    int top = 0;
    canvas.drawbitmap(background, left, top, null);

    canvas.drawbitmap(slideicon, slideiconleft, 0, null);
  }

  @override
  public boolean ontouchevent(motionevent event) {
    switch (event.getaction()) {
      case motionevent.action_down:
      case motionevent.action_move:
        slideiconleft = (int) (event.getx() - slideiconwidth / 2);

        if (slideiconleft < 0) {
          slideiconleft = 0;
        } else if (slideiconleft > maxslideiconleft) {
          slideiconleft = maxslideiconleft;
        }
        break;
      case motionevent.action_up:
        if (event.getx() < backgroundwidth / 2) {
          slideiconleft = 0;
          checkstate(false);
        } else {
          slideiconleft = maxslideiconleft;
          checkstate(true);
        }
        break;
    }
    invalidate();
    return true;
  }

  private void checkstate(boolean state) {
    if (this.state != state) {
      this.state = state;

      if (monstatechangedlistener != null) {
        monstatechangedlistener.onstatechanged(state);
      }
    }
  }
}


布局文件的编写:

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:huang="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <com.example.aswitch.mytogglebutton
    android:id="@+id/toggle_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    huang:slidebackground="@mipmap/slide_background2"
    huang:slideicon="@mipmap/slide_icon2"
    huang:state="false" />

</relativelayout>


为了使布局文件中的属性有作用,还要单独在values文件夹中写一个attrs.xml文件,声明相关的属性。

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="mytogglebutton">
    <attr name="slidebackground" format="reference" />
    <attr name="slideicon" format="reference" />
    <attr name="state" format="boolean" />
  </declare-styleable>
</resources> 

案例五 下拉刷新的listview:

功能介绍: 系统本身的listview默认情况下是没有下拉刷新和上拉加载更多的功能。但是listview有addheadview和addfootview方法,因此可以为listview加头布局或者脚布局。对于头布局来说,有三个状态,下拉刷新、松开刷新、正在刷新,同时伴有相应的动画予以提示。刷新成功后,为listview添加一套信息。同样的脚布局加载成功后,在listview的最后一条数据后面,在加载一条信息。

实现步骤:

         1. 写布局文件。这里除了在主界面中需要一个listview外,还需要一个头部的布局文件和一个尾部的布局文件。初始状态时,头布局和脚布局均是隐藏的。当滑动事件出发的时候,调整头布局和脚布局的位置。这里。头布局和脚布局的根表签最好是使用linearlayout进行包裹,否则在滑动显示是容易出现问题。
         2. 写一个类继承listview。初始化界面布局,通过view.measure(0, 0)方法主动触发测量,mesure内部会调用onmeasure,从而获取到头布局的高度。如步骤一所说,开始状态时,头布局是隐藏的,所以为头布局设置panding值,来使得头布局隐藏,只需要把setpadding中的第二个参数设置为负的头文件的高。同样的,显示头布局只需要把setpadding中的第二个参数设置为0。对于根布局的显示和隐藏同样采用上述的方法。当然也可以改变setpadding的第四个参数来控制显示和隐藏。
         3. 重写ontouchevent()方法。手指按下时,记录下初始位置。手指滑动时在记录一个y坐标。通过两次坐标的差值,判断手指滑动的方向。在可见的第一个条目的position为0的时候,如果手指向下滑动,有两种状态,一个是下拉刷新,指的是头布局从不可见到刚好全部可见,一个是松开刷新,头布局全部可见后继续向下拖动,就会进入松开刷新状态。另外还有一种状态就是正在刷新,这种状态下,头布局刚好停留在顶部,维持一段时间。实际开发中,正在刷新是向服务器请求网络。这里添加的是假数据。不同的状态之间的转换还伴随有动画效果,因此,这里还写有补间动画来实现向上和向下的效果。
         4. 写一个滑动监听事件,在监听事件中进行脚布局逻辑的编写。当满足以下三种情况的时候则显示脚布局,一个listview处于空闲状态,另一个是界面上可见的最后一条item是listview中最后的一条item,还有一个是如果当前没有去做正在加载更多的事情。显示脚布局的时候,不仅要设置padding值,同时选中listview最后一个条目,这样就可以在界面上完整的显示。
         5. 最后要注意的是,无论是同布局,还是脚布局,都要对状态进行修改。所以,可以增加一个回调监听。表明数据刷新或者加载完成,可以隐藏头布局和脚布局了,同时,头布局的状态恢复为下拉刷新,脚布局不是正在加载。同时,停掉相关的动画,修改显示的状态。

主程序(调用者)的编写。一般刷新是请求数据库数据,这里直接给出模拟数据演示效果。

package com.example.pulltofreshlistview;

import android.os.bundle;
import android.os.handler;
import android.support.v7.app.appcompatactivity;
import android.widget.arrayadapter;

import com.example.pulltofreshlistview.view.pulltorefreshlistview;

import java.util.arraylist;

public class mainactivity extends appcompatactivity {
  private pulltorefreshlistview listview;
  private arraylist<string> datas;
  private arrayadapter<string> adapter;

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    listview = (pulltorefreshlistview) findviewbyid(r.id.list_view);

    datas = new arraylist<string>();
    for (int i = 0; i < 20; i++) {
      datas.add("我又捡到钱了,好开心啊^_^ \t" + i);
    }

    adapter = new arrayadapter<string>(this, android.r.layout.simple_list_item_1, datas);
    listview.setadapter(adapter);

    listview.setonrefreshinglistener(new pulltorefreshlistview.onrefreshinglistener() {
      @override
      public void onrefreshing() {
        reloaddata();
      }

      @override
      public void onloadmore() {
        loadmore();
      }
    });
  }

  /**
   * 重新联网获取数据
   */
  protected void reloaddata() {
    new handler().postdelayed(new runnable() {

      @override
      public void run() {
        datas.add(0, "我是刷新出来的数据");
        adapter.notifydatasetchanged();
        listview.onrefreshcomplete();
      }
    }, 3000);
  }

  /**
   * 联网加载更多数据
   */
  protected void loadmore() {
    new handler().postdelayed(new runnable() {

      @override
      public void run() {
        datas.add("我是加载更多出来的数据");
        adapter.notifydatasetchanged();
        listview.onloadmorecomplete();
      }
    }, 3000);
  }
}     

下拉刷新listview的逻辑部分

package com.example.pulltofreshlistview.view;

import android.content.context;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import android.view.animation.rotateanimation;
import android.widget.abslistview;
import android.widget.imageview;
import android.widget.listview;
import android.widget.progressbar;
import android.widget.textview;

import com.example.pulltofreshlistview.r;

/**
 * created by huang on 2016/12/1.
 */


public class pulltorefreshlistview extends listview {

  private view headerview;
  private float downy;
  private int headerviewheight;
  private static final int state_pull_to_refresh = 0;
  private static final int state_release_refresh = 1;
  private static final int state_refreshing = 2;
  private int currentstate = state_pull_to_refresh;  // 默认是下拉刷新状态
  private imageview iv_arrow;
  private progressbar progress_bar;
  private textview tv_state;
  private rotateanimation upanim;
  private rotateanimation downanim;
  private onrefreshinglistener monrefreshinglistener;
  private view footerview;
  private int footerviewheight;
  private boolean loadingmore;

  public pulltorefreshlistview(context context, attributeset attrs) {
    super(context, attrs);
    initheaderview();
    initfooterview();
  }

  private void initheaderview() {
    headerview = view.inflate(getcontext(), r.layout.header_view, null);
    iv_arrow = (imageview) headerview.findviewbyid(r.id.iv_arrow);
    progress_bar = (progressbar) headerview.findviewbyid(r.id.progress_bar);
    showrefreshingprogressbar(false);
    tv_state = (textview) headerview.findviewbyid(r.id.tv_state);
    headerview.measure(0, 0);
    headerviewheight = headerview.getmeasuredheight();
    hideheaderview();
    super.addheaderview(headerview);
    upanim = createrotateanim(0f, -180f);
    downanim = createrotateanim(-180f, -360f);
  }

  private void initfooterview() {
    footerview = view.inflate(getcontext(), r.layout.footer_view, null);
    footerview.measure(0, 0);
    footerviewheight = footerview.getmeasuredheight();
    hidefooterview();
    super.addfooterview(footerview);

    super.setonscrolllistener(new onscrolllistener() {

      @override
      public void onscrollstatechanged(abslistview view, int scrollstate) {
        if (scrollstate == onscrolllistener.scroll_state_idle
            && getlastvisibleposition() == getcount() - 1
            && loadingmore == false
            ) {
          loadingmore = true;
          showfooterview();
          setselection(getcount() - 1);

          if (monrefreshinglistener != null) {
            monrefreshinglistener.onloadmore();
          }
        }
      }

      @override
      public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) {

      }
    });
  }

  private void hidefooterview() {
    int paddingtop = -footerviewheight;
    setfooterviewpaddingtop(paddingtop);
  }

  private void showfooterview() {
    int paddingtop = 0;
    setfooterviewpaddingtop(paddingtop);
  }

  private void setfooterviewpaddingtop(int paddingtop) {
    footerview.setpadding(0, paddingtop, 0, 0);
  }

  /**
   * 设置显示进度的圈圈
   *
   * @param showprogressbar 如果是true,则显示progressbar,否则的话显示箭头
   */
  private void showrefreshingprogressbar(boolean showprogressbar) {
    progress_bar.setvisibility(showprogressbar ? view.visible : view.gone);
    iv_arrow.setvisibility(!showprogressbar ? view.visible : view.gone);

    if (showprogressbar) {
      iv_arrow.clearanimation();  // 有动画的view要清除动画才能真正的隐藏
    }
  }

  /**
   * 创建旋转动画
   *
   * @param fromdegrees 从哪个角度开始转
   * @param todegrees  转到哪个角度
   * @return
   */
  private rotateanimation createrotateanim(float fromdegrees, float todegrees) {
    int pivotxtype = rotateanimation.relative_to_self;
    int pivotytype = rotateanimation.relative_to_self;
    float pivotxvalue = 0.5f;
    float pivotyvalue = 0.5f;
    rotateanimation ra = new rotateanimation(fromdegrees, todegrees, pivotxtype, pivotxvalue, pivotytype, pivotyvalue);
    ra.setduration(300);
    ra.setfillafter(true);
    return ra;
  }

  /**
   * 隐藏headerview
   */
  private void hideheaderview() {
    int paddingtop = -headerviewheight;
    setheaderviewpaddingtop(paddingtop);
  }

  /**
   * 显示headerview
   */
  private void showheaderview() {
    int paddingtop = 0;
    setheaderviewpaddingtop(paddingtop);
  }

  /**
   * 设置headerview的paddingtop
   *
   * @param paddingtop
   */
  private void setheaderviewpaddingtop(int paddingtop) {
    headerview.setpadding(0, paddingtop, 0, 0);
  }

  @override
  public boolean ontouchevent(motionevent ev) {
    switch (ev.getaction()) {
      case motionevent.action_down:
        downy = ev.gety();
        break;
      case motionevent.action_move:
        if (currentstate == state_refreshing) {
          return super.ontouchevent(ev);
        }

        int fingermovedistancey = (int) (ev.gety() - downy);    // 手指移动的距离
        if (fingermovedistancey > 0 && getfirstvisibleposition() == 0) {
          int paddingtop = -headerviewheight + fingermovedistancey;
          setheaderviewpaddingtop(paddingtop);

          if (paddingtop < 0 && currentstate != state_pull_to_refresh) {
            currentstate = state_pull_to_refresh;
            tv_state.settext("下拉刷新");
            iv_arrow.startanimation(downanim);
            showrefreshingprogressbar(false);
          } else if (paddingtop >= 0 && currentstate != state_release_refresh) {
            currentstate = state_release_refresh;
            tv_state.settext("松开刷新");
            iv_arrow.startanimation(upanim);
            showrefreshingprogressbar(false);

          }
          return true;
        }
        break;
      case motionevent.action_up:
        if (currentstate == state_release_refresh) {
          currentstate = state_refreshing;
          tv_state.settext("正在刷新");
          showrefreshingprogressbar(true);
          showheaderview();

          if (monrefreshinglistener != null) {
            monrefreshinglistener.onrefreshing();
          }
        } else if (currentstate == state_pull_to_refresh) {
          hideheaderview();
        }
        break;
    }
    return super.ontouchevent(ev);
  }

  public void setonrefreshinglistener(onrefreshinglistener monrefreshinglistener) {
    this.monrefreshinglistener = monrefreshinglistener;
  }

  /**
   * listview刷新的监听器
   */
  public interface onrefreshinglistener {
    void onrefreshing();

    void onloadmore();
  }

  /**
   * 联网刷新数据的操作已经完成了
   */
  public void onrefreshcomplete() {
    hideheaderview();
    currentstate = state_pull_to_refresh;
    showrefreshingprogressbar(false);
  }

  /**
   * 加载更多新数据的操作已经完成了
   */
  public void onloadmorecomplete() {
    hidefooterview();
    loadingmore = false;
  }
}


布局文件包含三个部分,listview主体部分:

<?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=".mainactivity">

  <com.example.pulltofreshlistview.view.pulltorefreshlistview
    android:id="@+id/list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</relativelayout>


头布局:

<?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="wrap_content"
  android:orientation="horizontal"
  android:gravity="center_vertical" >

  <relativelayout
    android:layout_width="50dp"
    android:layout_height="50dp">

    <imageview
      android:id="@+id/iv_arrow"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/arrow"
      android:layout_centerinparent="true"/>

    <progressbar
      android:id="@+id/progress_bar"
      style="@android:style/widget.progressbar"
      android:indeterminatedrawable="@drawable/progress_medium_red"
      android:layout_width="28dp"
      android:layout_height="28dp"
      android:layout_centerinparent="true"
      android:visibility="gone"/>

  </relativelayout>

  <linearlayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">

    <textview
      android:id="@+id/tv_state"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textcolor="#ff0000"
      android:text="下拉刷新"/>

    <textview
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textcolor="#666666"
      android:textsize="12sp"
      android:text="最后刷新时间:2015-07-25 19:59:39"
      android:layout_margintop="4dp"/>

  </linearlayout>

</linearlayout>


脚布局:

<?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="wrap_content"
  android:gravity="center"
  android:orientation="horizontal">

  <progressbar
    style="@android:style/widget.progressbar"
    android:layout_width="28dp"
    android:layout_height="28dp"
    android:layout_marginbottom="8dp"
    android:layout_margintop="8dp"
    android:indeterminatedrawable="@drawable/progress_medium_red" />

  <textview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginleft="6dp"
    android:text="加载更多..."
    android:textcolor="#ff0000" />

</linearlayout>


案例六 侧滑菜单:

功能分析: 之前的案例大部分都只有一个控件,因此是通过继承view实现。侧滑菜单显然需要两个控件才能实现,一个用于加载主界面,一个用于加载侧滑界面。所以这里需要继承viewgroup。通过在主界面滑动来控制界面的显示和隐藏。

实现步骤: 

          1. 布局文件的书写。布局文件包含两个部分,主界面和侧滑界面。主界面中有一个标题栏,标题栏有图片按钮,文字组成。图片按牛同样可以控制侧边栏的显示和隐藏。侧边栏在scrollview中添加一个linearlayout,在linearlayout中纵向排列textview显示文本。当然,侧滑菜单同样可以用listview显示,这里为简单起见就不采用listview显示。此外,textview要想响应点击事件,需要设置clickable为true。
          2. 创建一个类继承于viewgroup。重写onmeasure()方法,测量测量控件大小,包括两个子控件。这里,侧滑菜单的宽在布局文件中写好了,主界面的宽适配手机界面。所以分别调用menu.measure(menuwidth, heightmeasurespec)和main.measure(widthmeasurespec, heightmeasurespec)方法即可获取。排版容器中的子view。由于该自定义的控件包含不止一个子view,所以重写onlayout()方法是必不可少的。对子view进行排版,子view的0,0坐标是slidingmenu的左上角。对侧滑菜单进行排版,菜单的left坐标在负的菜单宽的位置,菜单的top坐标在0的位置,菜单的right坐标在0的位置,菜单的bottom坐标在容器的最底边。对主界面进行排版,主界面的left坐标在0的位置,主界面的top坐标在0的位置,主界面的right坐标在容器的最右边,主界面的bottom坐标在容器的最底边。
          3. 重写onintercepttouchevent。本案例中只关心水平划动,所以如果水平移动距离比垂直移动距离大,则认为是水平移动,把事件拦截,不让scrollview使用,此事件交由控件本身处理。这里有必要介绍一下事件分发。视图集合对于事件的分发,自上而下处理。viewgroup拥有下面3个touch相关方法,dispatchtouchevent(motionevent ev)用于touch事件的颁发,onintercepttouchevent(motionevent ev) 用于拦截touch事件,ontouchevent(motionevent event) 用于处理touch事件。这里重写了onintercepttouchevent(),相应的还要重写ontouchevent()方法。
          4. 在ontouchevent()方法中实现滑动跟随的逻辑。滑动事件中,只关心横向滑动。按下时,记录下当前的x坐标。滑动时,再次记录当前x坐标,两者相减得到移动的距离。通过scrollto()方法滑到相应的位置。系统给定的scrollto()方法默认向右为负值,所以可重写scrollto()方法保证移动为正值。代码优化。其实上述过程可以实现侧滑菜单的功能。当滑动过程略显生硬。这时就可以用到scroller,这个类专门用于模拟滚动的数值。同时要配合computescroll()方法使用。
          5. 还有一个菜单的开关按钮,要么开,要么关。开的时候完全显示侧滑菜单。关的时候,隐藏侧滑菜单。这点逻辑和ontouchevent()中手指抬起的逻辑一样。

主程序(调用者)的编写:

package com.example.slidingmenu;

import android.app.activity;
import android.os.bundle;
import android.view.view;
import android.view.window;
import android.widget.linearlayout;
import android.widget.textview;
import android.widget.toast;

public class mainactivity extends activity {

  private slidingmenu sliding_menu;
  private linearlayout ll_menu;
  private textview tv_news;

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    requestwindowfeature(window.feature_no_title);
    setcontentview(r.layout.activity_main);
    sliding_menu = (slidingmenu) findviewbyid(r.id.sliding_menu);
    ll_menu = (linearlayout) findviewbyid(r.id.ll_menu);
    tv_news = (textview) findviewbyid(r.id.tv_news);
    setcurrentselectedmenuitem(tv_news);
  }

  /** 设置当前选择的菜单item */
  private void setcurrentselectedmenuitem(view menuitem) {
    for (int i = 0; i < ll_menu.getchildcount(); i++) {
      view child = ll_menu.getchildat(i);
      child.setselected(child == menuitem);
    }
  }

  /** 菜单列表中的某个菜单项被单击了 */
  public void onmenuitemclick(view v) {
    textview textview = (textview) v;
    toast.maketext(this, textview.gettext(), toast.length_short).show();
    setcurrentselectedmenuitem(v);
  }

  /** 主界面上的菜单按钮被单击了 */
  public void onmenutoggleclick(view v) {
    sliding_menu.toggle();
  }

}


侧滑菜单的主体逻辑。

package com.example.slidingmenu;

import android.content.context;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import android.view.viewgroup;
import android.widget.scroller;

/**
 * created by huang on 2016/12/1.
 */
public class slidingmenu extends viewgroup {

  private view menu;
  private view main;
  private int menuwidth;
  private int downx;
  private int currentx;
  /** 这个类专门用于模拟滚动的数值 */
  private scroller scroller;

  public slidingmenu(context context, attributeset attrs) {
    super(context, attrs);
    scroller = new scroller(context);
  }

  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);  // 测量容器自己的宽高

    menu = getchildat(0); // 获取菜单容器
    main = getchildat(1); // 获取主界面容器

    menuwidth = menu.getlayoutparams().width;   // 获取菜单的宽

    // 测量菜单
    menu.measure(menuwidth, heightmeasurespec);

    // 测量主界面
    main.measure(widthmeasurespec, heightmeasurespec);
  }

  @override
  protected void onlayout(boolean changed, int l, int t, int r, int b) {
    int menuleft = -menuwidth;
    int menutop = 0;
    int menuright = 0;
    int menubottom = b - t;
    menu.layout(menuleft, menutop, menuright, menubottom);

    int mainleft = 0;
    int maintop = 0;
    int mainright = r - l;
    int mainbottom = b - t;
    main.layout(mainleft, maintop, mainright, mainbottom);
  }

  /**
   * 让界面滚动到x的位置,传正数往右移,传负往左移
   * @param x
   */
  public void scrollto(int x) {
    super.scrollto(-x, 0);
  }

  /** 获取当前滑动到的位置 */
  public int getmyscrollx() {
    return -super.getscrollx();
  }

  @override
  public boolean onintercepttouchevent(motionevent ev) {
    switch (ev.getaction()) {
      case motionevent.action_down:
        downy = (int) ev.gety();
        downx = (int) ev.getx();
        break;
      case motionevent.action_move:
        int distancex = math.abs((int) (ev.getx() - downx));
        int distancey = math.abs((int) (ev.gety() - downy));
        if (distancex > distancey) {
          return true;
        }
        break;
    }
    return super.onintercepttouchevent(ev);
  }

  @override
  public boolean ontouchevent(motionevent event) {
    switch (event.getaction()) {
      case motionevent.action_down:
        downx = (int) event.getx();
        break;
      case motionevent.action_move:
        int fingermovedistancex = (int) event.getx() - downx;
        int destx = currentx + fingermovedistancex;

        if (destx < 0) {
          destx = 0;
        } else if (destx > menuwidth){
          destx = menuwidth;
        }

        scrollto(destx);
        break;
      case motionevent.action_up:
        if (getmyscrollx() < menuwidth / 2) {
          startscroll(0);
        } else {
          startscroll(menuwidth);
        }

        break;
    }
    return true;
  }

  int count;
  private int downy;

  /**
   * 以动画的方式滚动到指定的位置
   *
   * @param destx 要滑动到哪里(目标位置)
   */
  private void startscroll(int destx) {
    currentx = destx;
    int startx = getmyscrollx();
    int distatncex = destx - startx;
    int duration = 800;
    scroller.startscroll(startx, 0, distatncex, 0, duration);
    invalidate();
  }

  @override
  public void computescroll() {
    if (scroller.computescrolloffset()) {
      int currx = scroller.getcurrx();
      scrollto(currx);
      invalidate();
      count++;
    }
    system.out.println("count = " + count);
  }

  /** 菜单的开关按钮,要么开,要么关 */
  public void toggle() {
    if (getmyscrollx() > 0) {
      startscroll(0);
    } else {
      startscroll(menuwidth);
    }
  }
}


 整体布局

<?xml version="1.0" encoding="utf-8"?>
<com.example.slidingmenu.slidingmenu xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/sliding_menu"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <!-- 在slidingmenu中的索引0 -->
  <include layout="@layout/menu" />

  <!-- 在slidingmenu中的索引1 -->
  <include layout="@layout/main" />

</com.example.slidingmenu.slidingmenu>


主界面布局

<?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:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@mipmap/top_bar_bg"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <imageview
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:onclick="onmenutoggleclick"
      android:src="@mipmap/main_back" />

    <view
      android:layout_width="1dp"
      android:layout_height="match_parent"
      android:layout_marginbottom="6dp"
      android:layout_margintop="6dp"
      android:background="@mipmap/top_bar_divider" />

    <textview
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginleft="12dp"
      android:text="新闻"
      android:textcolor="@android:color/white"
      android:textsize="34sp" />

  </linearlayout>

  <textview
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:text="为了一个小馒头,友谊的小船说翻就翻"
    android:textsize="30sp" />
</linearlayout>


侧滑界面布局

<?xml version="1.0" encoding="utf-8"?>
<scrollview xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="240dp"
  android:layout_height="match_parent"
  android:background="@mipmap/menu_bg">

  <linearlayout
    android:id="@+id/ll_menu"
    android:layout_width="240dp"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <textview
      android:id="@+id/tv_news"
      android:text="新闻"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_news"/>

    <textview
      android:text="订阅"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_read"/>

    <textview
      android:text="本地"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_local"/>

    <textview
      android:text="跟贴"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_ties"/>

    <textview
      android:text="图片"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_pics"/>

    <textview
      android:text="话题"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_ugc"/>

    <textview
      android:text="投票"
      style="@style/menu_item"
      android:drawableleft="@mipmap/tab_vote"/>

    <textview
  android:text="聚合阅读"
  style="@style/menu_item"
  android:drawableleft="@mipmap/tab_focus"/>

  </linearlayout>
  </scrollview>


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