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

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

概述案例四自定义开关:功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景。当滑动开关时,开关的滑块可跟随手指移动。当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保

案例四 自定义开关:

功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景。当滑动开关时,开关的滑块可跟随手指移动。当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保存开关的状态,将开关的状态回调给调用者。当然,上述功能系统给定的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,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,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);  }  /**   * 设置显示进度的圈圈   *   * @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,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"            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        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,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,distatnceX,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="新闻"            androID:drawableleft="@mipmap/tab_news"/>    <TextVIEw      androID:text="订阅"            androID:drawableleft="@mipmap/tab_read"/>    <TextVIEw      androID:text="本地"            androID:drawableleft="@mipmap/tab_local"/>    <TextVIEw      androID:text="跟贴"            androID:drawableleft="@mipmap/tab_tIEs"/>    <TextVIEw      androID:text="图片"            androID:drawableleft="@mipmap/tab_pics"/>    <TextVIEw      androID:text="话题"            androID:drawableleft="@mipmap/tab_ugc"/>    <TextVIEw      androID:text="投票"            androID:drawableleft="@mipmap/tab_Vote"/>    <TextVIEw  androID:text="聚合阅读"    androID:drawableleft="@mipmap/tab_focus"/>  </linearLayout>  </ScrollVIEw>

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

总结

以上是内存溢出为你收集整理的Android自定义控件案例汇总2(自定义开关、下拉刷新、侧滑菜单)全部内容,希望文章能够帮你解决Android自定义控件案例汇总2(自定义开关、下拉刷新、侧滑菜单)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1147541.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存