Android 使用ViewDragHelper实现向slidingMenu侧滑菜单的效果
16lz
2021-01-23
第一步:实现类
import com.android.view.tool.Logger;import android.content.Context;import android.support.v4.widget.ViewDragHelper;import android.support.v4.widget.ViewDragHelper.Callback;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;/** * DrawerLayout侧滑布局 * * @Project App_View * @Package com.android.view.dragview * @author chenlin * @version 1.0 * @Note TODO */public class SlideLayout extends ViewGroup { private static final String TAG = "SlideLayout"; /** 最小距离 */ private static final int MIN_DRAWER_MARGIN = 64; /** 最小移动速度 */ private static final int MIN_FLING_VELOCITY = 400; private ViewDragHelper mHelper; /** 左侧菜单 */ private View mLeftMenuView; /** 内容视图 */ private View mContentView; /** 最小移动速度 */ private float minVel; /** 菜单的偏移量 */ private float mLeftMenuOffset; private int mMinDrawerMargin; public SlideLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideLayout(Context context) { this(context, null); } public SlideLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 得到密度 float density = getResources().getDisplayMetrics().density; minVel = MIN_FLING_VELOCITY * density; mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f); // 初始化 mHelper = ViewDragHelper.create(this, 1.0f, cb); // 从左侧滑入 mHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); // 设置最新速度 mHelper.setMinVelocity(minVel); } private Callback cb = new Callback() { /** * 说明只有mLeftMenuView可以移动 */ @Override public boolean tryCaptureView(View child, int pointerId) { Logger.e(TAG, "tryCaptureView"); return child == mLeftMenuView; } /** * 触摸到左边时,capture mLeftMenuView */ @Override public void onEdgeDragStarted(int edgeFlags, int pointerId) { Logger.e(TAG, "onEdgeDragStarted"); mHelper.captureChildView(mLeftMenuView, pointerId); } /** * 水平方向移动的距离 */ @Override public int getViewHorizontalDragRange(View child) { return child == mLeftMenuView ? child.getWidth() : 0; } /** * 菜单移动时,用来设置菜单是否显示 */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // 1.先得到菜单的宽度 int childWidth = changedView.getWidth(); // 2.得到偏移量 float offset = (childWidth + left) * 1.0f / childWidth; // 3.设置是否可见 changedView.setVisibility(offset == 0 ? VISIBLE : GONE); // 4. mLeftMenuOffset = offset; // 5.视图改变了,重绘制 invalidate(); } /** * 手指放开时 主要用于设定左侧菜单的位置 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { // 1.得到宽度 int childWidth = releasedChild.getWidth(); // 2.得到偏移量 float offset = (childWidth + releasedChild.getLeft()) * 1.0f / childWidth; // 3.设置位置,如果在右边,则菜单移动到0,否则移动到最左侧 int finalLeft = xvel > 0 || xvel == 0 && offset > 0 ? 0 : -childWidth; mHelper.settleCapturedViewAt(finalLeft, releasedChild.getTop()); } /** * 计算left 我们的目标范围是-mLeft.getWidth 到 0 的宽度 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return Math.max(-child.getWidth(), Math.min(left, 0)); } @Override public int clampViewPositionVertical(View child, int top, int dy) { return super.clampViewPositionVertical(child, top, dy); } }; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { View leftMenuView = getChildAt(1); // 1.得到测量的size int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // 2.测量父控件 setMeasuredDimension(widthSize, heightSize); // 3.先设左菜单 MarginLayoutParams lp = (MarginLayoutParams) leftMenuView.getLayoutParams(); int leftMenuWidthSpec = getChildMeasureSpec(widthMeasureSpec, lp.leftMargin + lp.rightMargin + mMinDrawerMargin, lp.width); int leftMenuHeighSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height); // 4.测量菜单 leftMenuView.measure(leftMenuWidthSpec, leftMenuHeighSpec); // 5.右边的内容view View contentView = getChildAt(0); lp = (MarginLayoutParams) contentView.getLayoutParams(); int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - lp.leftMargin - lp.rightMargin, lp.width); int contentHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, lp.height); contentView.measure(contentWidthSpec, contentHeightSpec); // 6.把测量后的view赋值给定义的 mLeftMenuView = leftMenuView; mContentView = contentView; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 1.先获得子view View menuView = mLeftMenuView; View contentView = mContentView; // 2.进行内容定位 MarginLayoutParams lp = (MarginLayoutParams) contentView.getLayoutParams(); contentView.layout(lp.leftMargin, lp.topMargin, lp.leftMargin + mLeftMenuView.getMeasuredWidth(), lp.topMargin + mLeftMenuView.getMeasuredHeight()); // 3.菜单定位 lp = (MarginLayoutParams) menuView.getLayoutParams(); final int menuWidth = menuView.getMeasuredWidth(); int left = -menuWidth + (int) (menuWidth * mLeftMenuOffset); menuView.layout(left, lp.topMargin, left + menuView.getMeasuredWidth(), lp.topMargin + menuView.getMeasuredHeight()); } /** * 把事件交由mHelper处理 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { mHelper.processTouchEvent(ev); return true; } /** * 如果在移动,则不断的绘制 */ @Override public void computeScroll() { if (mHelper.continueSettling(true)) { invalidate(); } } /** * 关闭slide */ public void closeSlide() { View menuView = mLeftMenuView; mLeftMenuOffset = 0.0f; mHelper.smoothSlideViewTo(menuView, -menuView.getWidth(), menuView.getTop()); } /** * 打开slide */ public void openSlide() { View menuView = mLeftMenuView; mLeftMenuOffset = 1.0f; mHelper.smoothSlideViewTo(menuView, 0, menuView.getTop()); } /** * 设置全屏 */ @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } /** * 把布局的参数交由MarginLayoutParam来处理 */ @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); }}
第二步:实现类
/** * 实现侧滑效果 * * @Project App_View * @Package com.android.view.dragview * @author chenlin * @version 1.0 * @Date 2016年4月22日 * @Note TODO */public class SlideActivity extends ActionBarActivity { private SlideMenuFragment mMenuFragment; private SlideLayout mSlideLayout; private TextView mContentTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_slide); initViews(); initFragment(); } private void initViews() { mSlideLayout = (SlideLayout) findViewById(R.id.id_drawerlayout); mContentTv = (TextView) findViewById(R.id.id_content_tv); } private void initFragment() { FragmentManager fm = getSupportFragmentManager(); mMenuFragment = (SlideMenuFragment) fm.findFragmentById(R.id.id_container_menu); if (mMenuFragment == null) { mMenuFragment = new SlideMenuFragment(); fm.beginTransaction().add(R.id.id_container_menu, mMenuFragment).commit(); } mMenuFragment.setOnMenuItemSelectedListener(new SlideMenuFragment.OnMenuItemSelectedListener() { @Override public void menuItemSelected(String title) { mSlideLayout.closeSlide(); mContentTv.setText(title); } }); }}
布局文件
<?xml version="1.0" encoding="utf-8"?><com.android.view.dragview.SlideLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/id_drawerlayout" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- content --> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#44ff0000" android:clickable="true" > <TextView android:id="@+id/id_content_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="hello world" android:textSize="40sp" /> </RelativeLayout> <!-- menu --> <FrameLayout android:id="@+id/id_container_menu" android:layout_width="match_parent" android:layout_height="match_parent" > </FrameLayout></com.android.view.dragview.SlideLayout>
第四步:创建fragment
import com.android.view.R;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.ListFragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ListView;/** * 左侧菜单 * @Project App_View * @Package com.android.view.dragview * @author chenlin * @version 1.0 * @Date 2016年4月22日 * @Note TODO */public class SlideMenuFragment extends ListFragment { /**菜单个数*/ private static final int SIZE_MENU_ITEM = 3; /**菜单集合*/ private SlideMenuItem[] mItems = new SlideMenuItem[SIZE_MENU_ITEM]; private SlideMenuAdapter mAdapter; //---回调函数---------------------------------------------- public OnMenuItemSelectedListener mlistener; public interface OnMenuItemSelectedListener { void menuItemSelected(String title); } public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener listener) { mlistener = listener; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //初始化菜单项 SlideMenuItem menuItem = null; for (int i = 0; i < mItems.length; i++) { menuItem = new SlideMenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light); mItems[i] = menuItem; } } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); view.setBackgroundColor(0xffffffff); //加载数据 mAdapter = new SlideMenuAdapter(getActivity(), mItems); setListAdapter(mAdapter); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); if (mlistener != null) { SlideMenuItem item = (SlideMenuItem) getListAdapter().getItem(position); mlistener.menuItemSelected(item.text); } mAdapter.setSelected(position); }}
第四步:创建适配器
import android.content.Context;import android.graphics.Color;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ArrayAdapter;import android.widget.ImageView;import android.widget.TextView;import com.android.view.R;/** * 菜单适配器 * @Project App_View * @Package com.android.view.dragview * @author chenlin * @version 1.0 * @Note TODO */public class SlideMenuAdapter extends ArrayAdapter<SlideMenuItem> { private LayoutInflater mInflater; private int mSelected; public SlideMenuAdapter(Context context, SlideMenuItem[] objects) { super(context, -1, objects); mInflater = LayoutInflater.from(context); } public void setSelected(int position) { this.mSelected = position; notifyDataSetChanged(); } @Override public View getView(int position, View convertView, ViewGroup parent) { SlideMenuItem item = getItem(position); if (convertView == null) { convertView = mInflater.inflate(R.layout.item_left_menu, parent, false); } ImageView iv = (ImageView) convertView.findViewById(R.id.id_item_icon); TextView title = (TextView) convertView.findViewById(R.id.id_item_title); title.setText(item.text); iv.setImageResource(item.icon); convertView.setBackgroundColor(Color.TRANSPARENT); //如果是选中的 if (position == mSelected) { iv.setImageResource(item.iconSelected); convertView.setBackgroundColor(getContext().getResources().getColor(R.color.state_menu_item_selected)); } return convertView; }}
实体类:
/** * 菜单项属性值 * * @Project App_View * @Package com.android.view.dragview * @author chenlin * @version 1.0 * @Note TODO */public class SlideMenuItem { public boolean isSelected; //是否选中 public String text;//文本 public int icon;//图标 public int iconSelected;//被选中的图片 public SlideMenuItem(String text, boolean isSelected, int icon, int iconSelected) { this.text = text; this.isSelected = isSelected; this.icon = icon; this.iconSelected = iconSelected; }}
颜色选择器state_menu_item_selected.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@android:color/white"/> <!-- pressed --> <item android:drawable="@android:color/black"/> <!-- default --></selector>
数组文件arrays.xml
<?xml version="1.0" encoding="utf-8"?><resources> <string-array name="array_left_menu"> <item>首页</item> <item>新闻</item> <item>游戏</item> </string-array></resources>
更多相关文章
- Android使用控件Spinner实现下拉菜单列表
- Android 多级菜单的实现
- Android Dialog在底部显示且宽度match_parent
- Android 向菜单按钮说再见
- android 自定义Android菜单背景的代码
- android悬浮球,应用在所有软件之上,点击可以显示菜单
- Android实现伸缩弹力分布菜单效果
- Android中微信主界面菜单栏的布局实现代码
- 修改Android actionbar 溢出菜单按钮(OverflowButton)默认图标