Android自定义控件案例汇总2(自定义开关、下拉刷新、侧滑菜单)
案例四自定义开关:
功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景。当滑动开关时,开关的滑块可跟随手指移动。当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保存开关的状态,将开关的状态回调给调用者。当然,上述功能系统给定的switch控件也可以实现。
实现步骤:
1.写一个类继承view,重写两个参数的构造方法。在构造方法中指定工作空间,通过attrs.getAttributeResourceValue方法将java代码中的属性值和xml中的属性值联系起来。这样可以在xml文件中指定相关的属性值。重写onmeasure和ondraw方法,绘制图片。这里测量图片大小直接用setMeasuredDimension方法,获取图片本身的大小。
2.设置接口回调。对于图片来说,我们希望能够在调用者获取开关的状态,因此需要设置一个接口回调,用于监控开关的状态,当开关的状态发生变化时间调用。接口回调的优势在于调用者并不知道何时调用,所以在另一个文件中设置一个接口,在该文件触发事件。由于重写了接口的方法,因此,执行重写后的方法。这样就可以实现数据的回调。自定义控件中接口回调的应用较为广泛,几乎所有的控件都需要设置监听,且写法较为固定。
3.重写ontouchevent()方法。分析得知,开关由两部分组成,一部分是底座儿,一部分是划片而。当手指滑动时,划片儿应该跟随手指移动。当划片的左边小于底座左边坐标时,让划片左边的坐标和底座对齐,当划片的右边大于底座右边坐标时,让划片右边的坐标和底座对齐,这样保证划片不越界。当手指松开后,判断划片的中线坐标是在底座儿中线坐标的左边还是右边,以此来决定划片最终是停在左边还是右边。同时改变开关的状态,将开关的状态回调给控件的调用中,读取开关的状态。
代码实现。主程序(调用者)中的代码:
packagecom.example.aswitch;
importandroid.os.Bundle;
importandroid.support.v7.app.AppCompatActivity;
importandroid.widget.Toast;
publicclassMainActivityextendsAppCompatActivity{
privateMyToggleButtontoggleButton;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toggleButton=(MyToggleButton)findViewById(R.id.toggle_button);
toggleButton.setOnStateChangedListener(newMyToggleButton.OnStateChangedListener(){
@Override
publicvoidonStateChanged(booleanstate){
Toast.makeText(MainActivity.this,state?"开":"关",Toast.LENGTH_SHORT).show();
}
});
}
}
自定义开关的具体实现;
packagecom.example.aswitch;
importandroid.content.Context;
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.graphics.Canvas;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.View;
/**
*Createdbyhuangon2016/12/1.
*/
publicclassMyToggleButtonextendsView{
privateBitmapbackground;
privateBitmapslideIcon;
privatebooleanstate;
privateOnStateChangedListenermOnStateChangedListener;
privateintbackgroundWidth;
privateintbackgroundHeight;
privateintslideIconWidth;
privateintslideIconHeight;
privateintslideIconLeft;
privateintmaxSlideIconLeft;
publicMyToggleButton(Contextcontext,AttributeSetattrs){
super(context,attrs);
Stringnamespace="http://schemas.android.com/apk/res-auto";
intslideBackgroundResId=attrs.getAttributeResourceValue(namespace,"slideBackground",-1);
intslideIconResId=attrs.getAttributeResourceValue(namespace,"slideIcon",-1);
if(slideBackgroundResId!=-1&&slideIconResId!=-1){
setSwitchImage(slideBackgroundResId,slideIconResId);
}
booleanstate=attrs.getAttributeBooleanValue(namespace,"state",false);
setState(state);
}
/**
*设置开关的图片
*@paramslideBackgroundResId开关的背景图片资源id
*@paramslideIconResId开关上面的滑块icon
*/
publicvoidsetSwitchImage(intslideBackgroundResId,intslideIconResId){
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;
}
/**设置开关按钮的状态*/
publicvoidsetState(booleanstate){
checkState(state);
if(state){
slideIconLeft=maxSlideIconLeft;
}else{
slideIconLeft=0;
}
}
publicvoidsetOnStateChangedListener(OnStateChangedListenermOnStateChangedListener){
this.mOnStateChangedListener=mOnStateChangedListener;
}
/**开关按钮状态改变的监听器*/
publicinterfaceOnStateChangedListener{
voidonStateChanged(booleanstate);
}
/**
*对View进行测量的方法
*/
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
setMeasuredDimension(backgroundWidth,backgroundHeight);
}
/**把View画出来的方法
*@paramcanvas画布
**/
@Override
protectedvoidonDraw(Canvascanvas){
intleft=0;
inttop=0;
canvas.drawBitmap(background,left,top,null);
canvas.drawBitmap(slideIcon,slideIconLeft,0,null);
}
@Override
publicbooleanonTouchEvent(MotionEventevent){
switch(event.getAction()){
caseMotionEvent.ACTION_DOWN:
caseMotionEvent.ACTION_MOVE:
slideIconLeft=(int)(event.getX()-slideIconWidth/2);
if(slideIconLeft<0){
slideIconLeft=0;
}elseif(slideIconLeft>maxSlideIconLeft){
slideIconLeft=maxSlideIconLeft;
}
break;
caseMotionEvent.ACTION_UP:
if(event.getX()<backgroundWidth/2){
slideIconLeft=0;
checkState(false);
}else{
slideIconLeft=maxSlideIconLeft;
checkState(true);
}
break;
}
invalidate();
returntrue;
}
privatevoidcheckState(booleanstate){
if(this.state!=state){
this.state=state;
if(mOnStateChangedListener!=null){
mOnStateChangedListener.onStateChanged(state);
}
}
}
}
布局文件的编写:
<?xmlversion="1.0"encoding="utf-8"?> <RelativeLayoutxmlns: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文件,声明相关的属性。
<?xmlversion="1.0"encoding="utf-8"?> <resources> <declare-styleablename="MyToggleButton"> <attrname="slideBackground"format="reference"/> <attrname="slideIcon"format="reference"/> <attrname="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.最后要注意的是,无论是同布局,还是脚布局,都要对状态进行修改。所以,可以增加一个回调监听。表明数据刷新或者加载完成,可以隐藏头布局和脚布局了,同时,头布局的状态恢复为下拉刷新,脚布局不是正在加载。同时,停掉相关的动画,修改显示的状态。
主程序(调用者)的编写。一般刷新是请求数据库数据,这里直接给出模拟数据演示效果。
packagecom.example.pulltofreshlistview;
importandroid.os.Bundle;
importandroid.os.Handler;
importandroid.support.v7.app.AppCompatActivity;
importandroid.widget.ArrayAdapter;
importcom.example.pulltofreshlistview.view.PullToRefreshListView;
importjava.util.ArrayList;
publicclassMainActivityextendsAppCompatActivity{
privatePullToRefreshListViewlistView;
privateArrayList<String>datas;
privateArrayAdapter<String>adapter;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(PullToRefreshListView)findViewById(R.id.list_view);
datas=newArrayList<String>();
for(inti=0;i<20;i++){
datas.add("我又捡到钱了,好开心啊^_^\t"+i);
}
adapter=newArrayAdapter<String>(this,android.R.layout.simple_list_item_1,datas);
listView.setAdapter(adapter);
listView.setOnRefreshingListener(newPullToRefreshListView.OnRefreshingListener(){
@Override
publicvoidonRefreshing(){
reloadData();
}
@Override
publicvoidonLoadMore(){
loadMore();
}
});
}
/**
*重新联网获取数据
*/
protectedvoidreloadData(){
newHandler().postDelayed(newRunnable(){
@Override
publicvoidrun(){
datas.add(0,"我是刷新出来的数据");
adapter.notifyDataSetChanged();
listView.onRefreshComplete();
}
},3000);
}
/**
*联网加载更多数据
*/
protectedvoidloadMore(){
newHandler().postDelayed(newRunnable(){
@Override
publicvoidrun(){
datas.add("我是加载更多出来的数据");
adapter.notifyDataSetChanged();
listView.onLoadmoreComplete();
}
},3000);
}
}
下拉刷新listview的逻辑部分
packagecom.example.pulltofreshlistview.view;
importandroid.content.Context;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.animation.RotateAnimation;
importandroid.widget.AbsListView;
importandroid.widget.ImageView;
importandroid.widget.ListView;
importandroid.widget.ProgressBar;
importandroid.widget.TextView;
importcom.example.pulltofreshlistview.R;
/**
*Createdbyhuangon2016/12/1.
*/
publicclassPullToRefreshListViewextendsListView{
privateViewheaderView;
privatefloatdownY;
privateintheaderViewHeight;
privatestaticfinalintSTATE_PULL_TO_REFRESH=0;
privatestaticfinalintSTATE_RELEASE_REFRESH=1;
privatestaticfinalintSTATE_REFRESHING=2;
privateintcurrentState=STATE_PULL_TO_REFRESH;//默认是下拉刷新状态
privateImageViewiv_arrow;
privateProgressBarprogress_bar;
privateTextViewtv_state;
privateRotateAnimationupAnim;
privateRotateAnimationdownAnim;
privateOnRefreshingListenermOnRefreshingListener;
privateViewfooterView;
privateintfooterViewHeight;
privatebooleanloadingMore;
publicPullToRefreshListView(Contextcontext,AttributeSetattrs){
super(context,attrs);
initHeaderView();
initFooterView();
}
privatevoidinitHeaderView(){
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);
}
privatevoidinitFooterView(){
footerView=View.inflate(getContext(),R.layout.footer_view,null);
footerView.measure(0,0);
footerViewHeight=footerView.getMeasuredHeight();
hideFooterView();
super.addFooterView(footerView);
super.setOnScrollListener(newOnScrollListener(){
@Override
publicvoidonScrollStateChanged(AbsListViewview,intscrollState){
if(scrollState==OnScrollListener.SCROLL_STATE_IDLE
&&getLastVisiblePosition()==getCount()-1
&&loadingMore==false
){
loadingMore=true;
showFooterView();
setSelection(getCount()-1);
if(mOnRefreshingListener!=null){
mOnRefreshingListener.onLoadMore();
}
}
}
@Override
publicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){
}
});
}
privatevoidhideFooterView(){
intpaddingTop=-footerViewHeight;
setFooterViewPaddingTop(paddingTop);
}
privatevoidshowFooterView(){
intpaddingTop=0;
setFooterViewPaddingTop(paddingTop);
}
privatevoidsetFooterViewPaddingTop(intpaddingTop){
footerView.setPadding(0,paddingTop,0,0);
}
/**
*设置显示进度的圈圈
*
*@paramshowProgressBar如果是true,则显示ProgressBar,否则的话显示箭头
*/
privatevoidshowRefreshingProgressBar(booleanshowProgressBar){
progress_bar.setVisibility(showProgressBar?View.VISIBLE:View.GONE);
iv_arrow.setVisibility(!showProgressBar?View.VISIBLE:View.GONE);
if(showProgressBar){
iv_arrow.clearAnimation();//有动画的View要清除动画才能真正的隐藏
}
}
/**
*创建旋转动画
*
*@paramfromDegrees从哪个角度开始转
*@paramtoDegrees转到哪个角度
*@return
*/
privateRotateAnimationcreateRotateAnim(floatfromDegrees,floattoDegrees){
intpivotXType=RotateAnimation.RELATIVE_TO_SELF;
intpivotYType=RotateAnimation.RELATIVE_TO_SELF;
floatpivotXValue=0.5f;
floatpivotYValue=0.5f;
RotateAnimationra=newRotateAnimation(fromDegrees,toDegrees,pivotXType,pivotXValue,pivotYType,pivotYValue);
ra.setDuration(300);
ra.setFillAfter(true);
returnra;
}
/**
*隐藏HeaderView
*/
privatevoidhideHeaderView(){
intpaddingTop=-headerViewHeight;
setHeaderViewPaddingTop(paddingTop);
}
/**
*显示HeaderView
*/
privatevoidshowHeaderView(){
intpaddingTop=0;
setHeaderViewPaddingTop(paddingTop);
}
/**
*设置HeaderView的paddingTop
*
*@parampaddingTop
*/
privatevoidsetHeaderViewPaddingTop(intpaddingTop){
headerView.setPadding(0,paddingTop,0,0);
}
@Override
publicbooleanonTouchEvent(MotionEventev){
switch(ev.getAction()){
caseMotionEvent.ACTION_DOWN:
downY=ev.getY();
break;
caseMotionEvent.ACTION_MOVE:
if(currentState==STATE_REFRESHING){
returnsuper.onTouchEvent(ev);
}
intfingerMoveDistanceY=(int)(ev.getY()-downY);//手指移动的距离
if(fingerMoveDistanceY>0&&getFirstVisiblePosition()==0){
intpaddingTop=-headerViewHeight+fingerMoveDistanceY;
setHeaderViewPaddingTop(paddingTop);
if(paddingTop<0&¤tState!=STATE_PULL_TO_REFRESH){
currentState=STATE_PULL_TO_REFRESH;
tv_state.setText("下拉刷新");
iv_arrow.startAnimation(downAnim);
showRefreshingProgressBar(false);
}elseif(paddingTop>=0&¤tState!=STATE_RELEASE_REFRESH){
currentState=STATE_RELEASE_REFRESH;
tv_state.setText("松开刷新");
iv_arrow.startAnimation(upAnim);
showRefreshingProgressBar(false);
}
returntrue;
}
break;
caseMotionEvent.ACTION_UP:
if(currentState==STATE_RELEASE_REFRESH){
currentState=STATE_REFRESHING;
tv_state.setText("正在刷新");
showRefreshingProgressBar(true);
showHeaderView();
if(mOnRefreshingListener!=null){
mOnRefreshingListener.onRefreshing();
}
}elseif(currentState==STATE_PULL_TO_REFRESH){
hideHeaderView();
}
break;
}
returnsuper.onTouchEvent(ev);
}
publicvoidsetOnRefreshingListener(OnRefreshingListenermOnRefreshingListener){
this.mOnRefreshingListener=mOnRefreshingListener;
}
/**
*ListView刷新的监听器
*/
publicinterfaceOnRefreshingListener{
voidonRefreshing();
voidonLoadMore();
}
/**
*联网刷新数据的操作已经完成了
*/
publicvoidonRefreshComplete(){
hideHeaderView();
currentState=STATE_PULL_TO_REFRESH;
showRefreshingProgressBar(false);
}
/**
*加载更多新数据的操作已经完成了
*/
publicvoidonLoadmoreComplete(){
hideFooterView();
loadingMore=false;
}
}
布局文件包含三个部分,listview主体部分:
<?xmlversion="1.0"encoding="utf-8"?> <RelativeLayoutxmlns: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>
头布局:
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns: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-2519:59:39" android:layout_marginTop="4dp"/> </LinearLayout> </LinearLayout>
脚布局:
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns: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(MotionEventev)用于Touch事件的颁发,onInterceptTouchEvent(MotionEventev)用于拦截Touch事件,onTouchEvent(MotionEventevent)用于处理Touch事件。这里重写了onInterceptTouchEvent(),相应的还要重写ontouchevent()方法。
4.在ontouchevent()方法中实现滑动跟随的逻辑。滑动事件中,只关心横向滑动。按下时,记录下当前的x坐标。滑动时,再次记录当前x坐标,两者相减得到移动的距离。通过scrollTo()方法滑到相应的位置。系统给定的scrollTo()方法默认向右为负值,所以可重写scrollTo()方法保证移动为正值。代码优化。其实上述过程可以实现侧滑菜单的功能。当滑动过程略显生硬。这时就可以用到Scroller,这个类专门用于模拟滚动的数值。同时要配合computeScroll()方法使用。
5.还有一个菜单的开关按钮,要么开,要么关。开的时候完全显示侧滑菜单。关的时候,隐藏侧滑菜单。这点逻辑和ontouchevent()中手指抬起的逻辑一样。
主程序(调用者)的编写:
packagecom.example.slidingmenu;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.view.Window;
importandroid.widget.LinearLayout;
importandroid.widget.TextView;
importandroid.widget.Toast;
publicclassMainActivityextendsActivity{
privateSlidingMenusliding_menu;
privateLinearLayoutll_menu;
privateTextViewtv_news;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
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*/
privatevoidsetCurrentSelectedMenuItem(ViewmenuItem){
for(inti=0;i<ll_menu.getChildCount();i++){
Viewchild=ll_menu.getChildAt(i);
child.setSelected(child==menuItem);
}
}
/**菜单列表中的某个菜单项被单击了*/
publicvoidonMenuItemClick(Viewv){
TextViewtextView=(TextView)v;
Toast.makeText(this,textView.getText(),Toast.LENGTH_SHORT).show();
setCurrentSelectedMenuItem(v);
}
/**主界面上的菜单按钮被单击了*/
publicvoidonMenuToggleClick(Viewv){
sliding_menu.toggle();
}
}
侧滑菜单的主体逻辑。
packagecom.example.slidingmenu;
importandroid.content.Context;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.Scroller;
/**
*Createdbyhuangon2016/12/1.
*/
publicclassSlidingMenuextendsViewGroup{
privateViewmenu;
privateViewmain;
privateintmenuWidth;
privateintdownX;
privateintcurrentX;
/**这个类专门用于模拟滚动的数值*/
privateScrollerscroller;
publicSlidingMenu(Contextcontext,AttributeSetattrs){
super(context,attrs);
scroller=newScroller(context);
}
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
super.onMeasure(widthMeasureSpec,heightMeasureSpec);//测量容器自己的宽高
menu=getChildAt(0);//获取菜单容器
main=getChildAt(1);//获取主界面容器
menuWidth=menu.getLayoutParams().width;//获取菜单的宽
//测量菜单
menu.measure(menuWidth,heightMeasureSpec);
//测量主界面
main.measure(widthMeasureSpec,heightMeasureSpec);
}
@Override
protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
intmenuLeft=-menuWidth;
intmenuTop=0;
intmenuRight=0;
intmenuBottom=b-t;
menu.layout(menuLeft,menuTop,menuRight,menuBottom);
intmainLeft=0;
intmainTop=0;
intmainRight=r-l;
intmainBottom=b-t;
main.layout(mainLeft,mainTop,mainRight,mainBottom);
}
/**
*让界面滚动到x的位置,传正数往右移,传负往左移
*@paramx
*/
publicvoidscrollTo(intx){
super.scrollTo(-x,0);
}
/**获取当前滑动到的位置*/
publicintgetMyScrollX(){
return-super.getScrollX();
}
@Override
publicbooleanonInterceptTouchEvent(MotionEventev){
switch(ev.getAction()){
caseMotionEvent.ACTION_DOWN:
downY=(int)ev.getY();
downX=(int)ev.getX();
break;
caseMotionEvent.ACTION_MOVE:
intdistanceX=Math.abs((int)(ev.getX()-downX));
intdistanceY=Math.abs((int)(ev.getY()-downY));
if(distanceX>distanceY){
returntrue;
}
break;
}
returnsuper.onInterceptTouchEvent(ev);
}
@Override
publicbooleanonTouchEvent(MotionEventevent){
switch(event.getAction()){
caseMotionEvent.ACTION_DOWN:
downX=(int)event.getX();
break;
caseMotionEvent.ACTION_MOVE:
intfingerMoveDistanceX=(int)event.getX()-downX;
intdestX=currentX+fingerMoveDistanceX;
if(destX<0){
destX=0;
}elseif(destX>menuWidth){
destX=menuWidth;
}
scrollTo(destX);
break;
caseMotionEvent.ACTION_UP:
if(getMyScrollX()<menuWidth/2){
startScroll(0);
}else{
startScroll(menuWidth);
}
break;
}
returntrue;
}
intcount;
privateintdownY;
/**
*以动画的方式滚动到指定的位置
*
*@paramdestX要滑动到哪里(目标位置)
*/
privatevoidstartScroll(intdestX){
currentX=destX;
intstartX=getMyScrollX();
intdistatnceX=destX-startX;
intduration=800;
scroller.startScroll(startX,0,distatnceX,0,duration);
invalidate();
}
@Override
publicvoidcomputeScroll(){
if(scroller.computeScrollOffset()){
intcurrX=scroller.getCurrX();
scrollTo(currX);
invalidate();
count++;
}
System.out.println("count="+count);
}
/**菜单的开关按钮,要么开,要么关*/
publicvoidtoggle(){
if(getMyScrollX()>0){
startScroll(0);
}else{
startScroll(menuWidth);
}
}
}
整体布局
<?xmlversion="1.0"encoding="utf-8"?> <com.example.slidingmenu.SlidingMenuxmlns: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--> <includelayout="@layout/menu"/> <!--在SlidingMenu中的索引1--> <includelayout="@layout/main"/> </com.example.slidingmenu.SlidingMenu>
主界面布局
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns: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>
侧滑界面布局
<?xmlversion="1.0"encoding="utf-8"?> <ScrollViewxmlns: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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。