完美自定义SlidingMenu侧滑控件+沉浸式标题栏+解决华为虚拟键遮挡快速集成到项目

来源:互联网 发布:淘宝店铺导航不在了 编辑:程序博客网 时间:2024/05/09 21:24

最近项目用到了侧滑,然后自己写了一个SlidingMenu。前面2篇文章分别实现了2种方式的侧滑。一种是缩放带动画特效,另外一种是普通的侧滑。有不同需求的请分别点击查看自定义高级控件打造主流的侧滑菜单控件打造狂拽炫酷的主流自定义侧滑控件--仿酷狗和QQ5.0集成控件。

本篇在之前控件的基础上加以完善,增加了流畅度,滑动效果更好。下面先上一个效果图。效果完全不卡顿,滑动手感和流畅度胜过普通slidingMenu。本文旨在讲解如何使用该控件的过程,原理请阅读上面推荐的2篇。


该控件主要特点:

  • 速度达到某个值后自动滑动到指定位置
  • 拖动达到某位置后自动滑动到指定位置
  • 带有缩放,平移效果
  • 解决沉浸式下虚拟键把布局顶上的问题
  • 滑动手感调节请修改速度阈值和距离阈值,默认是3.0f的阈值,最大是8.0f,距离默认是屏幕的1/5.大家可以自己修改,建议80左右或者80以下,代码中修改
使用方法:
  • 下载nineoldandroids-2.4.0.jar包,Android studio可以通过下面方式导入,eclipse可以百度下载nineoldandroids-2.4.0.jar包。
    com.nineoldandroids:library:2.4.0

  • 新建一个widget包,将下面的SlidingMenu.java文件导入到该目录下,报红线错误,请修改为你的包名,ctrl+shift+O快速组织导入修正错误。
  • 在MainActivity里设置布局文件,请注意布局文件设置状态栏为透明。代码如下:
  • 布局文件的最外层即是一个SlidingMenu标签,里面是2个布局文件。一个菜单布局一个主界面布局。菜单布局建议使用fragment,主界面使用Activity或者fragment组合。
  • 菜单布局的高度设置为match_parent,宽度为wrap_content即可
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//透明状态栏getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}initView();initData();}


SlidingMenu.java完整代码:
package com.mero.wyt_register.widget; /** * */import android.content.Context;import android.content.res.Resources;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.Display;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.HorizontalScrollView;import android.widget.LinearLayout;import com.nineoldandroids.view.ViewHelper;import java.lang.reflect.Method;/** *@项目名称: SlidingMenu *@文件名称: SlidingMenu.java *@Date: 2016-9-23 *@Copyright: 2016 Technology Mero Inc. All rights reserved. *注意:由Mero开发 */public class SlidingMenu extends HorizontalScrollView{public static final String TAG = "SlidingMenu";private ViewGroup mMenuLayout;private ViewGroup mMainLayout;private int mScreenWidth;private int mScreenHeight;private int mPaddingRight = 100;private float downX;private float upX;private int mMenuWidth;private LinearLayout layout;private float d;private float inLeftOrRight;private int count = 0;private float velocityMax =0.0f;private boolean isNeedScrollByMaxVelocity = false;/** * @param context * @param attrs */public SlidingMenu(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stubWindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = wm.getDefaultDisplay();DisplayMetrics displayMetrics = new DisplayMetrics();display.getMetrics(displayMetrics);mScreenWidth = displayMetrics.widthPixels;mScreenHeight = displayMetrics.heightPixels;}/* * (non-Javadoc) * @see android.widget.HorizontalScrollView#onMeasure(int, int) */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {layout = (LinearLayout)getChildAt(0);//菜单布局mMenuLayout = (ViewGroup) layout.getChildAt(0);//主界面布局mMainLayout = (ViewGroup) layout.getChildAt(1);mMenuWidth = mScreenWidth - mPaddingRight;//菜单宽度Log.e("TAG","mMenuWidth:"+mMenuWidth+"");//设置菜单宽度mMenuLayout.getLayoutParams().width = mMenuWidth;//主布局宽度mMainLayout.getLayoutParams().width = mScreenWidth;super.onMeasure(widthMeasureSpec, heightMeasureSpec);}boolean flag  =false;//默认菜单menu隐藏/* * (non-Javadoc) * @see android.widget.HorizontalScrollView#onTouchEvent(android.view.MotionEvent) */@Overridepublic boolean onTouchEvent(MotionEvent ev) {VelocityTracker velocityTracker = VelocityTracker.obtain();//获取速度跟踪器velocityTracker.addMovement(ev);velocityTracker.computeCurrentVelocity(1,8.0f);switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downX = ev.getX();//按下的横坐标break;case MotionEvent.ACTION_UP:upX = ev.getX();//松开的横坐标d = upX - downX;//计算绝对值判断是否滑动float ds = Math.abs(d);if(flag==false){//左移if(d<0){this.smoothScrollTo(mMenuWidth, 0);flag = false;}//右移if(d>0){if(ds>mScreenWidth/5||isNeedScrollByMaxVelocity==true){this.smoothScrollTo(0, 0);flag = true;//显示}else{this.smoothScrollTo(mMenuWidth, 0);flag = false;//隐藏}}}else if(flag == true){//左移if(d<0){if(ds<mScreenWidth/5||isNeedScrollByMaxVelocity==true){this.smoothScrollTo(0, 0);flag = true;}else{this.smoothScrollTo(mMenuWidth, 0);flag = false;}}//右移if(d>0){this.smoothScrollTo(0, 0);flag = true;break;}}velocityMax = 0.0f;//恢复初始值isNeedScrollByMaxVelocity = false;//恢复初始状态return true;case MotionEvent.ACTION_MOVE :float velocityX = velocityTracker.getYVelocity();if(velocityX>3.0f){velocityMax = velocityX;}if(velocityMax>3.0f){isNeedScrollByMaxVelocity = true;}break;default:break;}return super.onTouchEvent(ev);}/* * (non-Javadoc) * @see android.widget.HorizontalScrollView#onLayout(boolean, int, int, int, int) */@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);flag = false;//虚拟键隐藏if(!checkDeviceHasNavigationBar(getContext())){this.scrollTo(mMenuWidth, 0);return;}//虚拟键弹出this.layout(0,0,mScreenWidth,mScreenHeight-getSwapKeyHeight(getContext()));this.scrollTo(mMenuWidth,0);}/* (non-Javadoc) * @see android.view.View#onScrollChanged(int, int, int, int) */@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {super.onScrollChanged(l, t, oldl, oldt);count++;Log.e(TAG,l+"进度l"+l);//缩放比例float scale = (float)l/mMenuWidth;Log.e(TAG, scale+"缩放度");//左菜单缩放float scaleLeft = 1-0.3f*scale;ViewHelper.setScaleX(mMenuLayout, scaleLeft);ViewHelper.setScaleY(mMenuLayout, scaleLeft);//透明度缩放float alphaLeft = 1- scale;ViewHelper.setAlpha(mMenuLayout, alphaLeft);//主界面float scaleRight = 0.93f + scale * 0.07f;ViewHelper.setScaleX(mMainLayout, scaleRight);ViewHelper.setScaleY(mMainLayout, scaleRight);//视觉差平移ViewHelper.setTranslationX(mMenuLayout, mMenuWidth * scale);}/** * 获得屏幕高度 */public static int getScreenHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);return outMetrics.heightPixels;}//获取屏幕原始尺寸高度,包括虚拟功能键高度public static int getDpi(Context context) {int dpi = 0;WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = windowManager.getDefaultDisplay();DisplayMetrics displayMetrics = new DisplayMetrics();@SuppressWarnings("rawtypes")Class c;try {c = Class.forName("android.view.Display");@SuppressWarnings("unchecked")Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);method.invoke(display, displayMetrics);dpi = displayMetrics.heightPixels;} catch (Exception e) {e.printStackTrace();}return dpi;}//判断是否开启虚拟键public static boolean checkDeviceHasNavigationBar(Context context) {boolean hasNavigationBar = false;Resources rs = context.getResources();int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");if (id > 0) {hasNavigationBar = rs.getBoolean(id);}try {Class systemPropertiesClass = Class.forName("android.os.SystemProperties");Method m = systemPropertiesClass.getMethod("get", String.class);String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");if ("1".equals(navBarOverride)) {hasNavigationBar = false;} else if ("0".equals(navBarOverride)) {hasNavigationBar = true;}} catch (Exception e) {}return hasNavigationBar;}//得到虚拟键的高度public static int getSwapKeyHeight(Context context){Log.e(TAG,"虚拟键高度是:"+(getDpi(context) -getScreenHeight(context)));return getDpi(context) -getScreenHeight(context);}/** * 获得状态栏的高度 * * @param context * @return */public static int getStatusHeight(Context context){int statusHeight = -1;try{Class<?> clazz = Class.forName("com.android.internal.R$dimen");Object object = clazz.newInstance();int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());statusHeight = context.getResources().getDimensionPixelSize(height);} catch (Exception e){e.printStackTrace();}return statusHeight;}}

main.xml
<com.mero.wyt_register.widget.SlidingMenu    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="wrap_content"    android:layout_height="match_parent"    android:scrollbars="none"    android:fadingEdge="none"    android:background="@drawable/background_install_xposed"    >    <!--上面的最后2行是为了去除滚动条和阴影效果-->    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="fill_parent"        android:orientation="horizontal" >//菜单界面布局        <fragment            android:id="@+id/fragment_menu"            android:name="com.mero.wyt_register.fragment.MenuFragment"            android:layout_width="wrap_content"            android:layout_height="fill_parent"            />//主界面布局        <include layout="@layout/activity_main"/>    </LinearLayout></com.mero.wyt_register.widget.SlidingMenu>


可参考的fragment代码如下(仅供参考):
MenuFragment .java
public class MenuFragment extends Fragment{    public static final String TAG  = "MenuFragment";    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.menu,container,false);        ImageView imageView01 = (ImageView) view.findViewById(R.id.img_memu_item_01);        return view;    }}

好了,如果还有不会用的请在下面留言或者写下问题发送邮件至我的邮箱790710371@qq.com。谢谢阅读!大家的支持就是我更新博客的动力

1 0