android 自定义布局之侧拉布局
来源:互联网 发布:建筑软件有哪些 编辑:程序博客网 时间:2024/05/17 22:09
android 自定义布局之侧拉布局
侧拉布局是一个自定义的布局,类似QQ的侧拉
代码如下,
package com.example.android_project_two;/** * Created by Administrator on 2017/6/21 0021. */import android.content.Context;import android.os.AsyncTask;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.WindowManager;import android.widget.LinearLayout;/** * @ 对外仅需设置的接口 * * -判断左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效 * boolean SlidingLayout.isLeftLayoutVisible() * * -将屏幕滚动到右侧布局界面 * void SlidingLayout.scrollToRightLayout() * * -将屏幕滚动到左侧布局界面 * void SlidingLayout.scrollToLeftLayout() * */public class SlidingLayout extends LinearLayout { private static final int SNAP_VELOCITY = 200; //滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。 private VelocityTracker mVelocityTracker; //用于计算手指滑动的速度。 private int touchSlop; //在被判定为滚动之前用户手指可以移动的最大值。 private int screenWidth; //屏幕宽度值 private int leftEdge ; //左边最多可以滑动到的左边缘,值由左边布局的宽度来定,marginLeft到达此值之后,不能再减少。 private int rightEdge = 0; //左边最多可以滑动到的右边缘,值恒为0,即marginLeft到达0之后,不能增加。 private float xDown; //记录手指按下时的横坐标。 private float yDown; //记录手指按下时的纵坐标。 private float xMove; //记录手指移动时的横坐标。 private float yMove; //记录手指移动时的纵坐标。 private float xUp; //记录手机抬起时显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。 private boolean isSliding; //是否正在滑动。的横坐标。 private boolean isLeftLayoutVisible; //左侧布局当前是 private MarginLayoutParams leftLayoutParams; //左侧布局的参数,通过此参数来重新确定左侧布局的宽度,以及更改leftMargin的值。 private MarginLayoutParams rightLayoutParams; //右侧布局的参数,通过此参数来重新确定右侧布局的宽度。 private View leftLayout; //左侧布局对象。 private View rightLayout; //右侧布局对象。 //构造函数 public SlidingLayout(Context context, AttributeSet attrs) { super(context, attrs); //获取屏幕宽度 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); screenWidth = wm.getDefaultDisplay().getWidth(); //获取滑动的最短距离 touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } //创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。 private void createVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } //拦截触摸事件 @Override public boolean onInterceptTouchEvent(MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: xDown = event.getRawX(); yDown = event.getRawY(); //当左边菜单完全显示时,点击右边的View,我们希望是拦截,而左边不拦截 if(xDown > leftLayout.getLeft()+leftLayout.getWidth() && isLeftLayoutVisible) return true; break; case MotionEvent.ACTION_MOVE: int distanceX=(int) (event.getRawX()-xDown); //水平滑动距离超过一定距离,拦截所有子view的touch事件,来处理自身onTouch事件来达到滑动的目的 if(Math.abs(distanceX)>=touchSlop) { return true; } break; } return false; } //触摸事件 @Override public boolean onTouchEvent(MotionEvent event) { // TODO 自动生成的方法存根 createVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下时,记录按下时的横坐标 xDown = event.getRawX(); yDown = event.getRawY(); break; case MotionEvent.ACTION_MOVE: // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整右侧布局的leftMargin值,从而显示和隐藏左侧布局 xMove = event.getRawX(); yMove = event.getRawY(); int distanceX = (int) (xMove - xDown); int distanceY = (int) (yMove - yDown); //只有触摸右边的View,才发生滑动 if(xMove > leftLayout.getLeft()+leftLayout.getWidth()) { //向右滑 if (!isLeftLayoutVisible && distanceX >= touchSlop && (isSliding || Math.abs(distanceY) <= touchSlop)) { isSliding = true; leftLayoutParams.leftMargin = leftEdge + distanceX; if (leftLayoutParams.leftMargin > rightEdge) { leftLayoutParams.leftMargin = rightEdge; } } //向左滑 else if (isLeftLayoutVisible && -distanceX >= touchSlop) { isSliding = true; leftLayoutParams.leftMargin = distanceX; if (leftLayoutParams.leftMargin < leftEdge) { leftLayoutParams.leftMargin = leftEdge; } } leftLayout.setLayoutParams(leftLayoutParams); } break; case MotionEvent.ACTION_UP: xUp = event.getRawX(); int upDistanceX = (int) (xUp - xDown); if (isSliding) { // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局 if (wantToShowLeftLayout()) { if (shouldScrollToLeftLayout()) { scrollToLeftLayout(); } else { scrollToRightLayout(); } } else if (wantToShowRightLayout()) { if (shouldScrollToRightLayout()) { scrollToRightLayout(); } else { scrollToLeftLayout(); } } } //在左侧菜单完全显示时,我们希望的是点击右边的View可以发生恢复 else if (upDistanceX < touchSlop && isLeftLayoutVisible && xUp > leftLayout.getLeft()+leftLayout.getWidth()) { scrollToRightLayout(); } recycleVelocityTracker(); break; } if (this.isEnabled()) { if (isSliding) { unFocusBindView(); return true; } if (isLeftLayoutVisible) { return true; } return false; } return true; } //将屏幕滚动到左侧布局界面,滚动速度设定为50. public void scrollToLeftLayout() { //传入第一个参数 new ScrollTask().execute(50); } //将屏幕滚动到右侧布局界面,滚动速度设定为-50. public void scrollToRightLayout() { //传入第一个参数 new ScrollTask().execute(-50); } //左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效。 public boolean isLeftLayoutVisible() { return isLeftLayoutVisible; } //在onLayout中重新设定左侧布局和右侧布局的参数。 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { // 获取左侧布局对象 leftLayout = getChildAt(0); leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams(); // 设置最左边距为负的左侧布局的宽度 leftEdge = -leftLayoutParams.width; leftLayoutParams.leftMargin = leftEdge; leftLayout.setLayoutParams(leftLayoutParams); // 获取右侧布局对象 rightLayout = getChildAt(1); rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams(); rightLayoutParams.width = screenWidth; rightLayout.setLayoutParams(rightLayoutParams); rightLayout.setClickable(true); } } //判断当前手势的意图是不是想显示右侧布局。如果手指移动的距离是负数,且当前左侧布局是可见的,则认为当前手势是想要显示右侧布局。 private boolean wantToShowRightLayout() { return xUp - xDown < 0 && isLeftLayoutVisible; } //判断当前手势的意图是不是想显示左侧布局。如果手指移动的距离是正数,且当前左侧布局是不可见的,则认为当前手势是想要显示左侧布局。 private boolean wantToShowLeftLayout() { return xUp - xDown > 0 && !isLeftLayoutVisible; } //判断是否应该滚动将左侧布局展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,就认为应该滚动将左侧布局展示出来。 private boolean shouldScrollToLeftLayout() { return xUp - xDown > leftLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } //判断是否应该滚动将右侧布局展示出来。如果手指移动距离加上leftLayoutPadding大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将右侧布局展示出来。 private boolean shouldScrollToRightLayout() { return xDown - xUp > leftLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } //获取手指在右侧布局的监听View上的滑动速度。 private int getScrollVelocity() { mVelocityTracker.computeCurrentVelocity(1000); int velocity = (int) mVelocityTracker.getXVelocity(); return Math.abs(velocity); } //回收VelocityTracker对象。 private void recycleVelocityTracker() { mVelocityTracker.recycle(); mVelocityTracker = null; } //使用可以获得焦点的控件在滑动的时候失去焦点。 private void unFocusBindView() { if (rightLayout != null) { rightLayout.setPressed(false); rightLayout.setFocusable(false); rightLayout.setFocusableInTouchMode(false); } }//********************************************************************************************************************* /** * @ 利用轻量级线程实现滑动 ,应用在手指松开的滑动动画 */ class ScrollTask extends AsyncTask<Integer, Integer, Integer> { //后台处理,入口参数对应第一个参数类型 @Override protected Integer doInBackground(Integer... speed) { int leftMargin = leftLayoutParams.leftMargin; // 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。 while (true) { leftMargin = leftMargin + speed[0]; if (leftMargin > rightEdge) { leftMargin = rightEdge; break; } if (leftMargin < leftEdge) { leftMargin = leftEdge; break; } //主动回调onProgressUpdate来更新界面(滑动) publishProgress(leftMargin); //使当前线程睡眠指定的毫秒数,为了要有滚动效果产生,每次循环使线程睡眠10毫秒,这样肉眼才能够看到滚动动画。 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } if (speed[0] > 0) { isLeftLayoutVisible = true; } else { isLeftLayoutVisible = false; } isSliding = false; //返回对应第三个参数类型,并且把值传入onPostExecute return leftMargin; } //调用publishProgress时,回调这个方法,用来更新界面(滑动),入口参数对应第二个参数类型 @Override protected void onProgressUpdate(Integer... leftMargin) { leftLayoutParams.leftMargin = leftMargin[0]; leftLayout.setLayoutParams(leftLayoutParams); //使用可以获得焦点的控件在滑动的时候失去焦点。 unFocusBindView(); } //在doInBackground执行完成后执行界面更新,入口参数对应第三个参数类型 @Override protected void onPostExecute(Integer leftMargin) { leftLayoutParams.leftMargin = leftMargin; leftLayout.setLayoutParams(leftLayoutParams); } }}
在xml方面的布局就直接引用它就行,这个代码注释也是很多,很容易懂
阅读全文
0 0
- android 自定义布局之侧拉布局
- Android 侧拉布局
- Android之自定义布局
- android之ListView自定义布局
- 侧拉布局
- Android 自定义控件之 继承布局文件
- Android 自定义布局之组合部件
- Android开发之自定义布局和控件
- Android之文件actionbar自定义布局
- Android 之自定义布局(继承控件)
- UICollectionView之自定义布局
- ToolBar之自定义布局
- android自定义布局
- Android自定义Notification布局
- android 自定义Preferecne布局
- android 自定义Preferecne布局
- android AlertDialog自定义布局
- Android自定义Gallery布局
- Canvas入门
- 关于删除mysql用户的问题
- UnityShader入门精要学习笔记(十三):光照衰减与Unity阴影
- 根据数据库自动生成java代码
- HTML中的列表标签和表单标签
- android 自定义布局之侧拉布局
- django 升级到1.13之后以前的项目报错
- Thread AsyncTask(异步任务)、Handler 更新UI
- form.js
- 170708 How to read csv file with pandas and select specified rows
- 170708 逆向-南邮CTF逆向(maze)
- 特征工程(三) 数据标准化和归一化
- messager.js
- jzoj 1278_排队_线段树