知识点提示:
一、在ViewGroup中,让自己内容移动有以下几个方法:
1 、layout(l,t,r,b);
2、offsetTopAndBottom(offset)和offsetLeftAndRight(offset);
3、scrollTo和scrollBy方法;
注意:滚动的并不是viewgroup内容本身,而是它的矩形边框
它是瞬间移动,
4、event.getX()和event.getRawX(),前者是触摸点相对于view左上角的坐标,后者是触摸点相对于屏幕左上角的坐标
5、View移动可以细分为2类:
a.view本身在父view中移动,即整体移动,同时该分类也可看做是父view的内容在移动
b.view的内容在移动,即内部移动
二、在自定义ViewGroup中一般不需要去实现onMeasure,
我们去实现系统已有的ViewGroup,比如FrameLayout,
它会帮我们区实现onMeasure方法
三、让view在一段时间内移动到某个位置
1、.使用自定义动画(让view在一段时间内做某件事)
2、.使用Scroller(模拟一个执行流程,)
效果图:
第一步:分别创建侧滑菜单(使用scrollView) 主页面 两个布局文件
layout_menu.xml
<?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="@drawable/menu_bg" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="300dp"> <TextView style="@style/MenuTabText" android:background="#33aa9900" android:drawableLeft="@drawable/tab_news" android:text="新闻" /> <TextView style="@style/MenuTabText" android:drawableLeft="@drawable/tab_read" android:text="订阅" /> <TextView style="@style/MenuTabText" android:drawableLeft="@drawable/tab_ties" android:text="跟帖" /> <TextView style="@style/MenuTabText" android:drawableLeft="@drawable/tab_pics" android:text="图片" /> <TextView style="@style/MenuTabText" android:drawableLeft="@drawable/tab_ugc" android:text="话题" /> <TextView style="@style/MenuTabText" android:drawableLeft="@drawable/tab_vote" android:text="投票" /> <TextView style="@style/MenuTabText" android:drawableLeft="@drawable/tab_focus" android:text="聚合阅读" /> </LinearLayout> </ScrollView>
layout_main.xml
<?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:background="#55666666" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:background="@drawable/top_bar_bg" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:id="@+id/btn_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/main_back" /> <View android:layout_width="1dp" android:layout_height="match_parent" android:layout_marginBottom="5dp" android:layout_marginTop="5dp" android:background="@drawable/top_bar_divider" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:text="xxxx新闻" android:textColor="#ffffff" android:textSize="22sp" /> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="。。。。。" android:textColor="#000000" android:textSize="30sp" /> </LinearLayout>
activity_main.xml
<?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="com.zaizai.sildeslipmenu.MainActivity"> <com.zaizai.sildeslipmenu.view.SlideMenu android:id="@+id/slideMenu" android:layout_width="match_parent" android:layout_height="match_parent" > <!--菜单界面--> <include layout="@layout/layout_menu"/> <!--主界面--> <include layout="@layout/layout_main"/> </com.zaizai.sildeslipmenu.view.SlideMenu> </RelativeLayout>
自定义动画类SlideMenu.java
package com.zaizai.sildeslipmenu.view; import android.view.View; import android.view.animation.Animation; import android.view.animation.Transformation; /** * 让指定view在一段时间内scrollTo到指定位置 * @author Administrator * */ public class ScrollAnimation extends Animation{ private View view; private int targetScrollX; private int startScrollX; private int totalValue; public ScrollAnimation(View view, int targetScrollX) { super(); this.view = view; this.targetScrollX = targetScrollX; startScrollX = view.getScrollX(); totalValue = this.targetScrollX - startScrollX; int time = Math.abs(totalValue); setDuration(time); } /** * 在指定的时间内一直执行该方法,直到动画结束 * interpolatedTime:0-1 标识动画执行的进度或者百分比 * time : 0 - 0.5 - 0.7 - 1 * value: 10 - 60 - 80 - 110 * 当前的值 = 起始值 + 总的差值*interpolatedTime */ @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); int currentScrollX = (int) (startScrollX + totalValue*interpolatedTime); view.scrollTo(currentScrollX, 0); } } 自定义类
package com.zaizai.sildeslipmenu.view; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.Scroller; /** * Created by zaizai on 2015/11/24. */ public class SlideMenu extends FrameLayout { private static final String TAG = "zaizai"; private ScrollAnimation animation; private View menuView; private View mainView; private int screenHight = 0; private int downX; private int menuWidth; private Scroller scroller; public SlideMenu(Context context) { super(context); init(); } public SlideMenu(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { scroller = new Scroller(getContext()); } /** * 当1级的子view全部加载完调用可以用初始化子view的引用 * 这里无法获取子view 的宽高 */ @Override protected void onFinishInflate() { super.onFinishInflate(); menuView = getChildAt(0); mainView = getChildAt(1); menuWidth = menuView.getLayoutParams().width; } /** * widthMeasureSpec heightMeasureSpec 是系统测量SildeMenu时传入的参数 * 这两个参数测量出的狂傲能让SlideMenu充满窗体,其实是正好等于屏幕宽高 */ /* @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //1、测量所有子view //通过getLayoutParams 可以获得布局文件中制定的宽高 menuView.measure(menuWidth, heightMeasureSpec); //2、直接使用SlideMenu的测量参数,因为他们的宽高都一样是充满父窗体 mainView.measure(widthMeasureSpec, heightMeasureSpec); }*/ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //l:当前子view距离父View坐标系中的X坐标 t:当前子View在父View坐标系中的Y坐标 // r: Log.i(TAG, "menuViewR:" + menuView.getMeasuredWidth() + "b:" + menuView.getMeasuredHeight()); Log.i(TAG, "mainViewR:" + mainView.getLayoutParams().width + "b:" + mainView.getLayoutParams().height); menuView.layout(-menuWidth, 0, 0, b); mainView.layout(0, t, r, b); } //拦截触摸事件,试之不传递给ScrollView处理。 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) ev.getX(); break; case MotionEvent.ACTION_MOVE: int deltaX = (int) (ev.getX() - downX); if (Math.abs(deltaX) > 8) { 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: { Log.i(TAG, "getScrollX:" + getScrollX()); /*gex()触摸屏幕的x坐标*/ Log.i(TAG, "getX:" + event.getX()); int moveX = (int) event.getX(); /*触摸移动的距离*/ int deltaX = moveX - downX; int newScaleX = (int) (getScrollX() - deltaX); if (newScaleX < -menuWidth) { newScaleX = -menuWidth; } if (newScaleX > 0) { newScaleX = 0; } downX = moveX; scrollTo(newScaleX, 0); break; } case MotionEvent.ACTION_UP: { if (getScrollX() > (-menuWidth) / 2) { /*关闭菜单*/ closeMenu(); } else { /*打开菜单*/ openMenu(); } break; } default: { break; } } return true; // return super.onTouchEvent(event); } private void openMenu() { /* scroller.startScroll(getScrollX(), 0, -menuWidth - getScrollX(), 0, 400); invalidate();*/ scrollTo(-menuWidth, 0); animation = new ScrollAnimation(this, -menuWidth); startAnimation(animation); } private void closeMenu() { scrollTo(0, 0); animation = new ScrollAnimation(this, 0); startAnimation(animation); /*scroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0, 400); invalidate();*/ } /** * Scroller不主动去调用这个方法 * 而invalidate()可以掉这个方法 * invalidate->draw->computeScroll */ /* @Override public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()) {//返回true,表示动画没结束 scrollTo(scroller.getCurrX(), 0); invalidate(); } }*/ /** * 切换菜单的开和关 */ public void switchMenu() { if (getScrollX() == 0) { //需要打开 openMenu(); } else { //需要关闭 closeMenu(); } } }
主activity
package com.zaizai.sildeslipmenu; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.Window; import android.widget.ImageView; import com.zaizai.sildeslipmenu.view.SlideMenu; public class MainActivity extends AppCompatActivity { private ImageView btnBack; private SlideMenu slideMenu; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnBack = (ImageView) findViewById(R.id.btn_back); slideMenu = (SlideMenu) findViewById(R.id.slideMenu); btnBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { slideMenu.switchMenu(); } }); } }
参考链接: