通过自定义ViewGroup我们能够实现很强大的页面切换效果,下面是我的一点分享
要自定义ViewGroup,就要先继承ViewGroup,然后实现其onLayout(),onMeasure(),onTouchEvent()方法,
其中onlayout()方法是用来定义子控件的布局
通过view.layout(getWidth()*i,0,getWidth()*(i+1),getHeight());方法依次将子view的布局大小添加到组合控件中去
onMeasure()方法用来测量子view的大小
通过view.measure(widthMeasureSpec, heightMeasureSpec);方法,依次计算子view的大小
onTouchEvent()响应点击事件和GestureDetector来协同判断页面的切换方式
此外通过onInterceptTouchEvent()来进行事件中断,解决子view和父view事件冲突的问题
具体实现代码如下:
public class MyViewGroup extends ViewGroup{
private Context context;
/**
* 定义手势识别工具类
*/
private GestureDetector gesture;
//系统的计算位移工具类
private Scroller scroller;
/**
* 是否发生快速滑动
*/
private boolean isFling;
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
private void initView() {
//scroller = new MyScroller(context);
scroller = new Scroller(context);
gesture = new GestureDetector(context, new OnGestureListener() {
/**
* 当有一个焦点抬起的时候调用
*/
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
/**
* 当手指按下的时候调用
*/
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
}
/**
* 当手指滑动的时候调用
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
/**
* 移动当前视图的内容
* disx x方向上移动的距离 为正时向左移动,为负时向右移动
* disy y方向上移动的距离
*/
scrollBy((int) distanceX, 0);
return false;
}
/**
* 当手指长按的时候调用
*/
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
}
/**
* 当手指快速滑动的时候调用
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
isFling = true;
if(velocityX>0&¤tId>0){//快速向右滑动
currentId--;
}else if(velocityX<0&¤tId<getChildCount()-1){//快速向左滑动
currentId++;
}
moveToCurrentView(currentId);
return false;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
});
}
/**
* 对view进行布局,确定子view的位置
* changed 为true时
* l\t\r\b指定子view在父view中的位置
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
for(int i = 0;i<getChildCount();i++){
//父View 会根据子view的大小和自身情况,综合确定子view的位置和大小
View view = getChildAt(i);
//指定子view的位置,是指在父view中的位置,左,上,右,下
view.layout(getWidth()*i,0,getWidth()*(i+1),getHeight());
}
}
/**
* 计算控件大小,还有一个任务就是计算子View 的大小,如果不计算就不会显示子view的视图
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for(int i=0;i<getChildCount();i++){
View view = getChildAt(i);
//计算子控件的大小
view.measure(widthMeasureSpec, heightMeasureSpec);
}
}
/**
* 记录当前屏幕显示的子view下标
*/
private int currentId = 0;
/**
* 记录鼠标点击下时的x坐标
*/
private int firstX;
/**
* 记录鼠标点击下时的x坐标
*/
private int firstY;
/**
* 中断点击事件,如果为true就会中断事件,执行自己的OnTouch事件
* 为false不中断,交给子view执行
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = false;
switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:
//点击时也要为手势注册监听,否则会接收不到点下事件
gesture.onTouchEvent(ev);
firstX = (int) ev.getX();
firstY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int dx = (int) (Math.abs(ev.getX()-firstX));
int dy = (int) (Math.abs(ev.getY()-firstY));
if(dx>dy&& dx>10){
result = true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
gesture.onTouchEvent(event);
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
firstX = (int) event.getX();
break;
case MotionEvent.ACTION_UP:
if(!isFling){//没有执行快速滑动的时候才执行按位置滑行
int nextId =0;
if((firstX - event.getX())>getWidth()/2){
nextId = currentId + 1;
}else if((event.getX()-firstX)>getWidth()/2){
nextId = currentId - 1;
}else{
nextId = currentId;
}
moveToCurrentView(nextId);
}
isFling = false;
break;
case MotionEvent.ACTION_MOVE:
break;
}
return true;
}
/**
* 移动到指定的位置上
* @param nextId
*/
public void moveToCurrentView(int nextId) {
//对nextId进行检验,避免出错
currentId = nextId>=0 ? nextId:0;
currentId = nextId>=(getChildCount()-1) ? (getChildCount()-1):nextId;
//瞬间移动
//scrollTo(currentId*getWidth(), 0);
if(mypagechangeListener!=null){
mypagechangeListener.movetoCurrentpage(currentId);
}
int distance = currentId*getWidth() - getScrollX();
/**
* 开始移动
* startX 开始时的x轴坐标
* startY 开始时的y轴坐标
* dx x轴滑动的距离
* dy y轴滑动的距离
* duration 运行时间,如果不指定则为默认
*/
scroller.startScroll(getScrollX(),0,distance,0);
//刷新界面
invalidate();
}
/**
* invalidate()会导致computeScroll()的执行
*/
@Override
public void computeScroll() {
//如果还在滑动
if(scroller.computeScrollOffset()){
int newx = (int) scroller.getCurrX();
scrollTo(newx, 0);
invalidate();
}
}
public MyPageChangeListener mypagechangeListener;
public MyPageChangeListener getMypagechangeListener() {
return mypagechangeListener;
}
public void setMypagechangeListener(MyPageChangeListener mypagechangeListener) {
this.mypagechangeListener = mypagechangeListener;
}
/**
* 页面改变的监听事件
*
*/
public interface MyPageChangeListener{
public void movetoCurrentpage(int currentId);
}
}
写好了自定义viewGroup之后就是使用了:
先来看布局文件:
<?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:id="@+id/ll_titles"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal">
</LinearLayout>
<com.test.MyViewGroup
android:id="@+id/mvg_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
接下来是主程序文件:
public class MyViewGroupActivity extends Activity {
private String[] titles = new String[]{"菜单1","菜单2","菜单3","菜单4","菜单5","菜单6"};
private MyViewGroup mvg_show;
private LinearLayout ll_titles;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_myviewgroup);
mvg_show = (MyViewGroup) findViewById(R.id.mvg_show);
ll_titles = (LinearLayout) findViewById(R.id.ll_titles);
for(int i =0 ;i <titles.length;i++){
View view = getLayoutInflater().inflate(R.layout.activity_test,null);
view.setBackgroundColor(Color.rgb(0, i*20+40, 0));
mvg_show.addView(view,i);
TextView tv = new TextView(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
params.setMargins(20, 5, 20, 5);
tv.setLayoutParams(params);
tv.setText(titles[i]);
tv.setId(i);
tv.setClickable(true);
tv.setFocusable(true);
if(i==0){
tv.setTextColor(Color.RED);
}else{
tv.setTextColor(Color.BLACK);
}
ll_titles.addView(tv);
}
//滑动页面显示对应的标题
mvg_show.setMypagechangeListener(new MyPageChangeListener(){
@Override
public void movetoCurrentpage(int currentId) {
for(int i =0 ;i <titles.length;i++){
if(i!=currentId){
((TextView)ll_titles.getChildAt(i)).setTextColor(Color.BLACK);
}
}
((TextView)ll_titles.getChildAt(currentId)).setTextColor(Color.RED);
}
});
//点击标题显示对应的页面
for(int i =0 ;i <titles.length;i++){
((TextView)ll_titles.getChildAt(i)).setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view) {
((TextView)ll_titles.getChildAt(view.getId())).setTextColor(Color.RED);
mvg_show.moveToCurrentView(view.getId());
}
});
}
}
}
最终的效果如下: