您的位置:首页 > 移动开发 > Android开发

Android自定义组件系列【3】自定义ViewGroup实现侧滑

2014-05-23 11:04 633 查看
有关自定义ViewGroup的文章已经很多了,我为什么写这篇文章,对于初学者或者对自定义组件比较生疏的朋友虽然可以拿来主义的用了,但是要一步一步的实现和了解其中的过程和原理才能真真脱离别人的代码,举一反三却不容易,很多博主其实不愿意一步一步的去写,这样很耗时,但是如果能对读者有帮助,能从这篇文章中学会自定义组件就达到我的目的了。

第一步:搭建框架来实现一个3/5和2/5分屏的界面,效果如下:






最外层是一个自定义的ViewGroup布局文件如下:

view
sourceprint?

01.
<strong>
package
com.example.testscrollto;


02.


03.
import
android.content.Context;


04.
import
android.util.AttributeSet;


05.
import
android.view.View;


06.
import
android.view.ViewGroup;


07.
import
android.view.View.MeasureSpec;


08.


09.
public
class
MyScrollView
extends
ViewGroup{


10.


11.
private
int
mWidth;


12.
private
int
mHeight;


13.


14.
private
float
mMenuWeight
 =
3
.0f
 /
5
;
//菜单界面比例


15.


16.
private
View
 mMenuView; 
//菜单界面


17.
private
View
 mPriView;
//内容界面


18.


19.
public
MyScrollView(Context
 context,AttributeSet attrs) {


20.
super
(context,
 attrs);


21.


22.
}


23.


24.
@Override


25.
protected
void
onLayout(
boolean
arg0,
int
arg1,
int
arg2,
int
arg3,
int
arg4)
 {


26.
System.out.println(
"执行了onLayout"
);


27.
mMenuView.layout(
0
,
0
,
 (
int
)(mWidth
* mMenuWeight),mHeight);


28.
mPriView.layout((
int
)(mWidth
* mMenuWeight),
0
,
mWidth,mHeight);


29.
}


30.


31.
@Override


32.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec)
 {


33.
System.out.println(
"执行了onMeasure"
);


34.
super
.onMeasure(widthMeasureSpec,
 heightMeasureSpec);


35.
/*


36.
*
 onMeasure传入的两个参数是由上一层控件传入的大小,有多种情况,重写该方法时需要对计算控件的实际大小,


37.
*
然后调用setMeasuredDimension(int,int)设置实际大小。


38.
*
 onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。


39.
*
 我们需要通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,


40.
*
 用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。


41.
*
mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED,MeasureSpec.EXACTLY,MeasureSpec.AT_MOST。


42.
*
 MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。


43.
*
 MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。


44.
*
 MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。


45.
*/


46.
mWidth
 = MeasureSpec.getSize(widthMeasureSpec);
//获取MyScrollView的宽度


47.
mHeight
 = MeasureSpec.getSize(heightMeasureSpec);
//获取MyScrollView的高度


48.
}


49.


50.
/**设置右滑的菜单View*/


51.
public
void
setMenu(View
 menu){


52.
mMenuView
 = menu;


53.
addView(mMenuView);


54.
}


55.


56.
/**


57.
*
 设置主界面View


58.
*/


59.
public
void
setPrimary(View
 primary){


60.
mPriView
 = primary;


61.
addView(mPriView);


62.
}


63.


64.
}


65.
</strong>


第二步:按需要设置界面位置

view
sourceprint?

1.
<strong>
@Override


2.
protected
void
onLayout(
boolean
arg0,
int
arg1,
int
arg2,
int
arg3,
int
arg4)
 {


3.
mMenuView.layout(-(
int
)(mWidth
* mMenuWeight),
0
,
0
,
 mHeight);


4.
mPriView.layout(
0
,
0
,
mWidth,mHeight);


5.
}</strong>




第三步:实现左右滑动

view
sourceprint?

001.
<strong>
package
com.example.testscrollto;


002.


003.
import
android.content.Context;


004.
import
android.util.AttributeSet;


005.
import
android.view.MotionEvent;


006.
import
android.view.View;


007.
import
android.view.ViewGroup;


008.
import
android.widget.Scroller;


009.


010.
public
class
MyScrollView
extends
ViewGroup{


011.


012.
private
Context
 mContext;


013.
private
int
mWidth;


014.
private
int
mHeight;


015.


016.
private
float
mMenuWeight
 =
3
.0f
 /
5
;
//菜单界面比例


017.


018.
private
View
 mMenuView; 
//菜单界面


019.
private
View
 mPriView;
//内容界面


020.


021.
private
boolean
mIsShowMenu;


022.


023.
private
Scroller
 mScroller;


024.


025.


026.
public
MyScrollView(Context
 context,AttributeSet attrs) {


027.
super
(context,
 attrs);


028.
mContext
 = context;


029.
mScroller
 =
new
Scroller(mContext);


030.
}


031.


032.
@Override


033.
protected
void
onLayout(
boolean
arg0,
int
arg1,
int
arg2,
int
arg3,
int
arg4)
 {


034.
mMenuView.layout(-(
int
)(mWidth
* mMenuWeight),
0
,
0
,
 mHeight);


035.
mPriView.layout(
0
,
0
,
mWidth,mHeight);


036.
}


037.


038.
@Override


039.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec)
 {


040.
super
.onMeasure(widthMeasureSpec,
 heightMeasureSpec);


041.
mWidth
 = MeasureSpec.getSize(widthMeasureSpec);
//获取MyScrollView的宽度


042.
mHeight
 = MeasureSpec.getSize(heightMeasureSpec);
//获取MyScrollView的高度


043.
}


044.


045.
/**设置右滑的菜单View*/


046.
public
void
setMenu(View
 menu){


047.
mMenuView
 = menu;


048.
addView(mMenuView);


049.
}


050.


051.
/**


052.
*
 设置主界面View


053.
*/


054.
public
void
setPrimary(View
 primary){


055.
mPriView
 = primary;


056.
addView(mPriView);


057.
}


058.


059.
private
float
mDownX;


060.


061.
@Override


062.
public
boolean
onTouchEvent(MotionEvent
 event) {


063.
float
x
 = event.getX();


064.
switch
(event.getAction())
 {


065.
case
MotionEvent.ACTION_DOWN:


066.
System.out.println(
"ACTION_DOWN"
);


067.
mDownX
 = x;      
//记录按下时的x坐标


068.
break
;


069.
case
MotionEvent.ACTION_UP:


070.
System.out.println(
"ACTION_UP"
);


071.
int
dis
 = (
int
)
 (x - mDownX); 
//滑动的距离


072.
if
(Math.abs(dis)
>(mWidth * mMenuWeight /
2
)){


073.
if
(dis
>
0
){
//如果>0则是向右滑动


074.
showMenu();


075.
}
else
{
//如果<0则是向左滑动


076.
hideMenu();


077.
}


078.
}


079.
break
;


080.
default
:


081.
break
;


082.
}


083.


084.
return
true
;


085.
}


086.


087.
@Override


088.
public
void
computeScroll()
 {


089.
super
.computeScroll();


090.
if
(mScroller.computeScrollOffset()){


091.
scrollTo(mScroller.getCurrX(),
 mScroller.getCurrY());


092.
postInvalidate();


093.
}


094.
}


095.


096.
public
boolean
isShowMenu(){


097.
return
mIsShowMenu;


098.
}


099.


100.
public
void
showMenu(){


101.
if
(mIsShowMenu){


102.
return
;


103.
}


104.
mIsShowMenu
 =
true
;
//标记菜单已经显示


105.
int
dx
 = (
int
)(mWidth
 * mMenuWeight);
//滑动到目标位置的距离


106.
mScroller.startScroll(getScrollX(),
0
,
-dx,
0
,
500
);


107.
invalidate();


108.
}


109.


110.
public
void
hideMenu(){


111.
if
(!mIsShowMenu){


112.
return
;


113.
}


114.
mIsShowMenu
 =
false
;


115.
int
dx
 = (
int
)(mWidth
 * mMenuWeight);


116.
mScroller.startScroll(getScrollX(),
0
,
dx,
0
,
500
);


117.
invalidate();


118.
}


119.


120.


121.
}


122.
</strong>


从上面代码中可以看到下面两句代码触发computeScroll()方法

view
sourceprint?

1.
<strong>mScroller.startScroll(getScrollX(),
0
,
-dx,
0
,
500
);


2.
invalidate();</strong>


第四步:添加窗口状态切换监听接口

view
sourceprint?

1.
<strong>
public
interface
OnMenuChangedListener{


2.
public
void
onChanged(
boolean
isShow);


3.
}


4.


5.
public
void
setOnMenuChangedListener(OnMenuChangedListener
 listener){


6.
mListener
 = listener;


7.
}</strong>


将showMenu()方法和hideMenu()方法修改如下:

view
sourceprint?

01.
<strong>
public
void
showMenu(){


02.
if
(mIsShowMenu){


03.
return
;


04.
}


05.
mIsShowMenu
 =
true
;


06.
int
dx
 = (
int
)(mWidth
 * mMenuWeight);


07.
mScroller.startScroll(getScrollX(),
0
,
-dx,
0
,
500
);


08.
if
(mListener
 !=
null
){


09.
mListener.onChanged(mIsShowMenu);


10.
}


11.
invalidate();


12.
}


13.


14.
public
void
hideMenu(){


15.
if
(!mIsShowMenu){


16.
return
;


17.
}


18.
mIsShowMenu
 =
false
;


19.
int
dx
 = (
int
)(mWidth
 * mMenuWeight);


20.
mScroller.startScroll(getScrollX(),
0
,
dx,
0
,
500
);


21.
if
(mListener
 !=
null
){


22.
mListener.onChanged(mIsShowMenu);


23.
}


24.
invalidate();


25.
}</strong>


在MainActivity中添加监听

view
sourceprint?

1.
<strong> 
mScrollView.setOnMenuChangedListener(
new
OnMenuChangedListener()
 {


2.


3.
@Override


4.
public
void
onChanged(
boolean
isShow)
 {


5.
System.out.println(
"窗口切换了一次"
);


6.
}


7.
});</strong>


第五步:根据具体业务及需求实现菜单列表及界面(直接贴出代码)

MainActivity.java

view
sourceprint?

01.
<strong>
package
com.example.testrefreshview;


02.


03.


04.
import
android.app.Activity;


05.
import
android.graphics.Color;


06.
import
android.os.Bundle;


07.
import
android.view.View;


08.
import
android.view.View.OnClickListener;


09.
import
android.widget.AdapterView;


10.
import
android.widget.AdapterView.OnItemClickListener;


11.
import
android.widget.ArrayAdapter;


12.
import
android.widget.Button;


13.
import
android.widget.ListView;


14.


15.
import
com.example.testrefreshview.RightScrollView.OnMenuChangedListener;


16.


17.
/**


18.
*
 测试具有右滑菜单功能的ViewGroup,RigthScrollView


19.
*@author
 Lqh


20.
*/


21.
public
class
MainActivity
extends
Activity
 {


22.


23.
private
RightScrollView
 mRightScrollView;


24.
private
Button
 mShowMenuBtn;


25.
private
ListView
 mMenuList;


26.
private
ArrayAdapter<String>
 mAdapter;


27.
private
String[]
 menus = {
"附近的人"
,
"我的资料"
,
"设置"
,
"游戏"
,
"即时聊天"
};


28.


29.
@Override


30.
protected
void
onCreate(Bundle
 savedInstanceState) {


31.
super
.onCreate(savedInstanceState);


32.
setContentView(R.layout.rightscrollview_test);


33.
mRightScrollView
 = (RightScrollView)findViewById(R.id.rightscrollview);


34.
final
View
menu = getLayoutInflater().inflate(R.layout.rightscrollview_menu,
null
);


35.
final
View
primary = getLayoutInflater().inflate(R.layout.rightscrollview_primary,
null
);


36.
mMenuList
 = (ListView) menu.findViewById(R.id.list_right_menu);


37.
mShowMenuBtn
 = (Button) primary.findViewById(R.id.btn_showmenu);


38.
mAdapter
 =
new
ArrayAdapter<String>(
this
,
android.R.layout.simple_list_item_1,menus);


39.
mMenuList.setAdapter(mAdapter);


40.


41.
mShowMenuBtn.setOnClickListener(
new
OnClickListener()
 {


42.
public
void
onClick(View
 v) {


43.
if
(mRightScrollView.isShowMenu()){


44.
mRightScrollView.hideMenu();


45.
}
else
{


46.
mRightScrollView.showMenu();


47.
}


48.
}


49.
});


50.


51.
mRightScrollView.setOnMenuChangedListener(
new
OnMenuChangedListener()
 {


52.
public
void
onChanged(
boolean
isShow)
 {


53.
if
(isShow){


54.
mShowMenuBtn.setText(
"隐藏菜单"
);


55.
}
else
{


56.
mShowMenuBtn.setText(
"显示菜单"
);


57.
}


58.
}


59.
});


60.


61.
mRightScrollView.setMenu(menu);


62.
mRightScrollView.setPrimary(primary);


63.


64.
mMenuList.setOnItemClickListener(
new
OnItemClickListener()
 {


65.
public
void
onItemClick(AdapterView<?>
parent,View view,
int
position,
long
id)
 {


66.
switch
(position){


67.
case
0
:


68.
primary.setBackgroundColor(Color.CYAN);


69.
break
;


70.
case
1
:


71.
primary.setBackgroundColor(Color.BLUE);


72.
break
;


73.
case
2
:


74.
primary.setBackgroundColor(Color.GRAY);


75.
break
;


76.
case
3
:


77.
primary.setBackgroundColor(Color.MAGENTA);


78.
break
;


79.
case
4
:


80.
primary.setBackgroundColor(Color.YELLOW);


81.
break
;


82.
}


83.
mRightScrollView.hideMenu();


84.
}


85.
});


86.


87.
}


88.
}</strong>


RightScrollView.java

view
sourceprint?

001.
<strong>
package
com.example.testrefreshview;


002.


003.
import
android.content.Context;


004.
import
android.util.AttributeSet;


005.
import
android.view.MotionEvent;


006.
import
android.view.View;


007.
import
android.view.ViewGroup;


008.
import
android.widget.Scroller;


009.


010.
/**


011.
*
 具有右滑菜单的ViewGroup,类似于Facebook的主界面


012.
*
 @author Lqh


013.
*/


014.
public
class
RightScrollView
extends
ViewGroup
 {


015.


016.
private
Context
 mContext;


017.
private
Scroller
 mScroller;


018.
private
View
 mMenuView;


019.
private
View
 mPriView;


020.
private
int
mWidth;


021.
private
int
mHeight;


022.
private
boolean
mIsShowMenu;


023.
private
float
mMenuWeight
 =
3
.0f
 /
5
;


024.
private
OnMenuChangedListener
 mListener;


025.


026.


027.
public
RightScrollView(Context
 context) {


028.
this
(context,
null
);


029.
}


030.


031.
public
RightScrollView(Context
context,AttributeSet attrs) {


032.
super
(context,
 attrs);


033.


034.
mContext
 = context;


035.
mScroller
 =
new
Scroller(mContext);


036.


037.
}


038.


039.
/**设置右滑的菜单View*/


040.
public
void
setMenu(View
 menu){


041.
mMenuView
 = menu;


042.
addView(mMenuView);


043.
}


044.


045.
/**


046.
*
 设置主界面View


047.
*/


048.
public
void
setPrimary(View
 primary){


049.
mPriView
 = primary;


050.
addView(mPriView);


051.
}


052.


053.
public
boolean
isShowMenu(){


054.
return
mIsShowMenu;


055.
}


056.


057.
public
void
setOnMenuChangedListener(OnMenuChangedListener
 listener){


058.
mListener
 = listener;


059.
}


060.


061.


062.
public
void
showMenu(){


063.
if
(mIsShowMenu){


064.
return
;


065.
}


066.
mIsShowMenu
 =
true
;


067.
int
dx
 = (
int
)(mWidth
 * mMenuWeight);


068.
mScroller.startScroll(getScrollX(),
0
,
-dx,
0
,
500
);


069.
if
(mListener
 !=
null
){


070.
mListener.onChanged(mIsShowMenu);


071.
}


072.
invalidate();


073.
}


074.


075.
public
void
hideMenu(){


076.
if
(!mIsShowMenu){


077.
return
;


078.
}


079.
mIsShowMenu
 =
false
;


080.
int
dx
 = (
int
)(mWidth
 * mMenuWeight);


081.
mScroller.startScroll(getScrollX(),
0
,
dx,
0
,
500
);


082.
if
(mListener
 !=
null
){


083.
mListener.onChanged(mIsShowMenu);


084.
}


085.
invalidate();


086.
}


087.


088.
private
float
mDownX;


089.


090.
@Override


091.
public
boolean
onTouchEvent(MotionEvent
 event) {


092.


093.
float
x
 = event.getX();


094.


095.
switch
(event.getAction()){


096.
case
MotionEvent.ACTION_DOWN:


097.
mDownX
 = x;


098.
break
;


099.
case
MotionEvent.ACTION_UP:


100.
int
dis
 = (
int
)
 (x - mDownX);


101.
if
(Math.abs(dis)
>(mWidth * mMenuWeight /
2
)){


102.
if
(dis
>
0
){


103.
showMenu();


104.
}
else
{


105.
hideMenu();


106.
}


107.
}


108.
break
;


109.
}


110.


111.
return
true
;


112.
}


113.


114.


115.
@Override


116.
public
void
computeScroll()
 {


117.
super
.computeScroll();


118.
if
(mScroller.computeScrollOffset()){


119.
scrollTo(mScroller.getCurrX(),
 mScroller.getCurrY());


120.
postInvalidate();


121.
}


122.
}


123.


124.


125.
@Override


126.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec)
 {


127.


128.
mWidth
 = MeasureSpec.getSize(widthMeasureSpec);


129.
mHeight
 = MeasureSpec.getSize(heightMeasureSpec);


130.


131.
setMeasuredDimension(mWidth,
 mHeight);


132.
int
widthSpec
 = MeasureSpec.makeMeasureSpec((
int
)(mWidth
* mMenuWeight),MeasureSpec.EXACTLY);


133.
int
heightSpec
= MeasureSpec.makeMeasureSpec(mHeight,MeasureSpec.EXACTLY);


134.
mMenuView.measure(widthSpec,
 heightSpec);


135.


136.
widthSpec
= MeasureSpec.makeMeasureSpec(mWidth,MeasureSpec.EXACTLY);


137.
mPriView.measure(widthSpec,
 heightSpec);


138.
}


139.


140.
@Override


141.
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b)
 {


142.


143.
mMenuView.layout(-(
int
)(mWidth
* mMenuWeight),
0
,
0
,
 mHeight);


144.
mPriView.layout(
0
,
0
,
mWidth,mHeight);


145.
}


146.


147.
public
interface
OnMenuChangedListener{


148.
public
void
onChanged(
boolean
isShow);


149.
}


150.


151.


152.
}


153.
</strong>


运行效果:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: