本文共 9016 字,大约阅读时间需要 30 分钟。
在公司做开发的时候接到一个任务就是实现侧滑效果(酷狗比较老的版本的那种策划效果,QQ6.0以上的版本也是侧滑效果),遇到这种侧滑效果的话怎么实现?
首先应该考虑到以下几种办法:
1,使用系统自带的DrawerLayout这个控件
2,自定义ViewGroup+手势处理类(实现起来代码量有点多)
3,自定义ScrollView
在这里我们使用第三种方式自定义ScrollView的方式来实现。首先我们可以直观的想一下侧滑效果的布局,手指按在屏幕上往右滑,此时下层的布局就会展示出来,并且由小到大,上面的能够往右滑的这个布局逐渐变小,现在也就明白了,我们需要定义两个布局,假设上面的布局叫做内容布局,命名为layout_home_content,下面的布局命名为layout_home_menu,先暂时不考虑缩放问题,当前首要的任务就是把两个布局实现,并且能够滑动。下面是两个布局的具体实现代码:
layout_home_content.xml(这个布局很简单,只放了一个TextView)
layout_home_menu.xml(这个布局相对复杂一点,主要分为三大块,最上面是用户信息,中间放了一个ListView来显示能够点击的条目,最下面是退出按钮)
在定义完了上面两个布局之后,在主布局activity_main中引用这两个布局,其中SlidingMenu就是我们自定义的继承自ScrollView的滑动布局,如果你的包名和我的不一样,需要修改为你的完整包名。
好了,现在我们把布局绘制完成了(其中布局中的图片资源不再上传,大家可以随便找一个代替),现在运行一下:
为什么出现这样的结果呢?home_content布局这么窄的原因是因为home_content布局的宽高自定为wrap_content,放放在自定义的ScrollView中形成的。
接下来开始解决问题:
1,自定内容页面的宽度:
这里需要用到一个onFinishInflate(),这个方法是在布局解析完成后调用
rt android.view.ViewGroup;import android.view.WindowManager;import android.widget.HorizontalScrollView;/** * Created by yly on 2019/8/10. */public class SlidingMenu extends HorizontalScrollView { private int menuWidth; public SlidingMenu(Context context) { super(context); } public SlidingMenu(Context context, AttributeSet attrs) { super(context, attrs); } public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取自定义的右边一小块的宽度 TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.SlidingMenu); float rightMargin=typedArray.getDimension(R.styleable.SlidingMenu_menuRightMargin,dip2px(context,50)); typedArray.recycle(); menuWidth= (int) (getScreenWidth(context)-rightMargin); } @Override protected void onFinishInflate() { super.onFinishInflate(); //1,指定内容页面的宽度是整个屏幕的宽度, ViewGroup container= (ViewGroup) getChildAt(0);//这是SlidingMenu布局下的第一层LinearLayout布局 int childCount=container.getChildCount(); if (childCount>2){ throw new RuntimeException("不能放置超过两个布局"); } // 2,菜单页面的宽度等于屏幕的宽度减去右边一小步分的宽度(menuRightMargin) View menuView=container.getChildAt(0); ViewGroup.LayoutParams menuLayoutParams=menuView.getLayoutParams(); menuLayoutParams.width=menuWidth; menuView.setLayoutParams(menuLayoutParams); //7.0以下的手机不加这一行是可以的,但是高版就不行了 View contentView=container.getChildAt(1); ViewGroup.LayoutParams contentLayoutParams=contentView.getLayoutParams(); contentLayoutParams.width=getScreenWidth(getContext()); contentView.setLayoutParams(contentLayoutParams); } private int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * 获得屏幕高度 * * @param context * @return */ private int getScreenWidth(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.widthPixels; }}
好了,现在运行一下,内容页面正常显示了。接下来处理页面滑动,和缩放问题:(代码中有详细的注释)
package com.example.myproject; import android.content.Context; import android.content.res.TypedArray; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.HorizontalScrollView;/** * Email 240336124@qq.com * Created by Darren on 2017/6/19. * Version 1.0 * Description: */public class SlidingMenu extends HorizontalScrollView { // 菜单的宽度 private int mMenuWidth; private View mContentView,mMenuView; // GestureDetector 处理快速滑动 public SlidingMenu(Context context) { this(context, null); } public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 初始化自定义属性 TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu); float rightMargin = array.getDimension( R.styleable.SlidingMenu_menuRightMargin, dip2px(context, 50)); // 菜单页的宽度是 = 屏幕的宽度 - 右边的一小部分距离(自定义属性) mMenuWidth = (int) (getScreenWidth(context) - rightMargin); array.recycle(); } // 1.宽度不对(乱套了),指定宽高 @Override protected void onFinishInflate() { // 这个方法是布局解析完毕也就是 XML 布局文件解析完毕 super.onFinishInflate(); // 指定宽高 1.内容页的宽度是屏幕的宽度 // 获取LinearLayout ViewGroup container = (ViewGroup) getChildAt(0); int childCount = container.getChildCount(); if (childCount != 2) { throw new RuntimeException("只能放置两个子View!"); } mMenuView = container.getChildAt(0); // 设置只能通过 LayoutParams , ViewGroup.LayoutParams menuParams = mMenuView.getLayoutParams(); menuParams.width = mMenuWidth; // 7.0 以下的手机必须采用下面的方式 mMenuView.setLayoutParams(menuParams); // 2.菜单页的宽度是 屏幕的宽度 - 右边的一小部分距离(自定义属性) mContentView = container.getChildAt(1); ViewGroup.LayoutParams contentParams = mContentView.getLayoutParams(); contentParams.width = getScreenWidth(getContext()); mContentView.setLayoutParams(contentParams); // 2. 初始化进来是关闭 发现没用 // scrollTo(mMenuWidth,0); } // 4. 处理右边的缩放,左边的缩放和透明度,需要不断的获取当前滚动的位置 @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); Log.e("TAG", "l -> " + l);// 变化是 mMenuWidth - 0 // 算一个梯度值 float scale = 1f * l / mMenuWidth;// scale 变化是 1 - 0 // 右边的缩放: 最小是 0.7f, 最大是 1f float rightScale = 0.7f + 0.3f * scale; // 设置右边的缩放,默认是以中心点缩放 // 设置缩放的中心点位置 ViewCompat.setPivotX(mContentView,0); ViewCompat.setPivotY(mContentView, mContentView.getMeasuredHeight() / 2); ViewCompat.setScaleX(mContentView,rightScale); ViewCompat.setScaleY(mContentView, rightScale); // 菜单的缩放和透明度 // 透明度是 半透明到完全透明 0.5f - 1.0f float leftAlpha = 0.5f + (1-scale)*0.5f; ViewCompat.setAlpha(mMenuView,leftAlpha); // 缩放 0.7f - 1.0f float leftScale = 0.7f + (1-scale)*0.3f; ViewCompat.setScaleX(mMenuView,leftScale); ViewCompat.setScaleY(mMenuView, leftScale); // 最后一个效果 退出这个按钮刚开始是在右边,安装我们目前的方式永远都是在左边 // 设置平移,先看一个抽屉效果 // ViewCompat.setTranslationX(mMenuView,l); // 平移 l*0.7f ViewCompat.setTranslationX(mMenuView, 0.25f*l); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // 2. 初始化进来是关闭 scrollTo(mMenuWidth, 0); } // 3.手指抬起是二选一,要么关闭要么打开 @Override public boolean onTouchEvent(MotionEvent ev) { // 1. 获取手指滑动的速率,当期大于一定值就认为是快速滑动 , GestureDetector(系统提供好的类) // 2. 处理事件拦截 + ViewGroup 事件分发的源码实践 // 当菜单打开的时候,手指触摸右边内容部分需要关闭菜单,还需要拦截事件(打开情况下点击内容页不会响应点击事件) if (ev.getAction() == MotionEvent.ACTION_UP) { // 只需要管手指抬起 ,根据我们当前滚动的距离来判断 int currentScrollX = getScrollX(); if (currentScrollX > mMenuWidth / 2) { // 关闭 closeMenu(); } else { // 打开 openMenu(); } // 确保 super.onTouchEvent() 不会执行 return true; } return super.onTouchEvent(ev); } /** * 打开菜单 滚动到 0 的位置 */ private void openMenu() { // smoothScrollTo 有动画 smoothScrollTo(0, 0); } /** * 关闭菜单 滚动到 mMenuWidth 的位置 */ private void closeMenu() { smoothScrollTo(mMenuWidth, 0); } /** * 获得屏幕高度 * * @param context * @return */ private int getScreenWidth(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.widthPixels; } /** * Dip into pixels */ private int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}
最后的效果就是
// 1. 获取手指滑动的速率,当于一定值就认为是快速滑动 , GestureDetector(系统提供好的类)// 2. 处理事件拦截 + ViewGroup 事件分发的源码实践// 当菜单打开的时候,手指触摸右边内容部分需要关闭菜单,还需要拦截事件(打开情况下点击内容页不会响应点击事件)
转载地址:https://blog.csdn.net/yaoyaoyao_123/article/details/99095765 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!