ViewDragHelper实战 自己打造Drawerlayout
转载请标明出处:
http://blog.csdn.net/lmj623565791/article/details/47396187;
本文出自:【张鸿洋的博客】
一、概述
中间拖了蛮长时间了,在上一篇我们介绍了ViewDragHelper,详情:ViewDragHelper完全解析,当然了,上一篇都是小示例的形式去演示代码功能,并不能给人一种实用的感觉。那么,本篇博客就准备实用ViewDragHelper来实现一个DrawerLayout的效果,当然了,大家也可以选择直接去看Drawerlayout的源码。相信侧滑大家肯定不陌生,网络上流传无数个版本,其实利用ViewDragHelper就能方便的写出比较不错的侧滑代码~
那么首先上个效果图:
ok,其实和DrawerLayout效果类似,当然不是完全的一致~~本篇的主要目的也是演示VDH的实战用法,大家在选择侧滑的时候还是建议使用DrawerLayout.
二、实战
(一)布局文件
首先看一下布局文件:
<com.zhy.learn.view.LeftDrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:id="@+id/id_drawerlayout" android:layout_height="match_parent"
>
<!--content-->
<RelativeLayout
android:clickable="true"
android:layout_width="match_parent"
android:background="#44ff0000"
android:layout_height="match_parent">
<TextView
android:id="@+id/id_content_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world"
android:textSize="40sp"
android:layout_centerInParent="true"/>
</RelativeLayout>
<!--menu-->
<FrameLayout
android:id="@+id/id_container_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</FrameLayout>
</com.zhy.learn.view.LeftDrawerLayout>
ok,可以看到我们的LeftDrawerLayout里面一个是content,一个是menu~为了方便,我们就默认child0为content,child1为menu。
(二)LeftDrawerLayout
package com.zhy.learn.view;
import android.content.Context;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.zhy.util.common.L;
/**
* Created by zhy on 15/5/29.
*/
public class LeftDrawerLayout extends ViewGroup
{
private static final int MIN_DRAWER_MARGIN = 64; // dp
/**
* Minimum velocity that will be detected as a fling
*/
private static final int MIN_FLING_VELOCITY = 400; // dips per second
/**
* drawer离父容器右边的最小外边距
*/
private int mMinDrawerMargin;
private View mLeftMenuView;
private View mContentView;
private ViewDragHelper mHelper;
/**
* drawer显示出来的占自身的百分比
*/
private float mLeftMenuOnScrren;
public LeftDrawerLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
//setup drawer's minMargin
final float density = getResources().getDisplayMetrics().density;
final float minVel = MIN_FLING_VELOCITY * density;
mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);
mHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()
{
@Override
public int clampViewPositionHorizontal(View child, int left, int dx)
{
int newLeft = Math.max(-child.getWidth(), Math.min(left, 0));
return newLeft;
}
@Override
public boolean tryCaptureView(View child, int pointerId)
{
L.e("tryCaptureView");
return child == mLeftMenuView;
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId)
{
L.e("onEdgeDragStarted");
mHelper.captureChildView(mLeftMenuView, pointerId);
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel)
{
L.e("onViewReleased");
final int childWidth = releasedChild.getWidth();
float offset = (childWidth + releasedChild.getLeft()) * 1.0f / childWidth;
mHelper.settleCapturedViewAt(xvel > 0 || xvel == 0 && offset > 0.5f ? 0 : -childWidth, releasedChild.getTop());
invalidate();
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)
{
final int childWidth = changedView.getWidth();
float offset = (float) (childWidth + left) / childWidth;
mLeftMenuOnScrren = offset;
//offset can callback here
changedView.setVisibility(offset == 0 ? View.INVISIBLE : View.VISIBLE);
invalidate();
}
@Override
public int getViewHorizontalDragRange(View child)
{
return mLeftMenuView == child ? child.getWidth() : 0;
}
});
//设置edge_left track
mHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
//设置minVelocity
mHelper.setMinVelocity(minVel);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
View leftMenuView = getChildAt(1);
MarginLayoutParams lp = (MarginLayoutParams)
leftMenuView.getLayoutParams();
final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
mMinDrawerMargin + lp.leftMargin + lp.rightMargin,
lp.width);
final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
lp.topMargin + lp.bottomMargin,
lp.height);
leftMenuView.measure(drawerWidthSpec, drawerHeightSpec);
View contentView = getChildAt(0);
lp = (MarginLayoutParams) contentView.getLayoutParams();
final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
contentView.measure(contentWidthSpec, contentHeightSpec);
mLeftMenuView = leftMenuView;
mContentView = contentView;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
View menuView = mLeftMenuView;
View contentView = mContentView;
MarginLayoutParams lp = (MarginLayoutParams) contentView.getLayoutParams();
contentView.layout(lp.leftMargin, lp.topMargin,
lp.leftMargin + contentView.getMeasuredWidth(),
lp.topMargin + contentView.getMeasuredHeight());
lp = (MarginLayoutParams) menuView.getLayoutParams();
final int menuWidth = menuView.getMeasuredWidth();
int childLeft = -menuWidth + (int) (menuWidth * mLeftMenuOnScrren);
menuView.layout(childLeft, lp.topMargin, childLeft + menuWidth,
lp.topMargin + menuView.getMeasuredHeight());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
boolean shouldInterceptTouchEvent = mHelper.shouldInterceptTouchEvent(ev);
return shouldInterceptTouchEvent;
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
mHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll()
{
if (mHelper.continueSettling(true))
{
invalidate();
}
}
public void closeDrawer()
{
View menuView = mLeftMenuView;
mLeftMenuOnScrren = 0.f;
mHelper.smoothSlideViewTo(menuView, -menuView.getWidth(), menuView.getTop());
}
public void openDrawer()
{
View menuView = mLeftMenuView;
mLeftMenuOnScrren = 1.0f;
mHelper.smoothSlideViewTo(menuView, 0, menuView.getTop());
}
@Override
protected LayoutParams generateDefaultLayoutParams()
{
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p)
{
return new MarginLayoutParams(p);
}
}
哈,很少的代码就完成了我们的侧滑的编写,目测如果使用横向的LinearLayout代码更短。构造方法中,主要就是初始化我们的mDragHelper了,接下来onMeasure和onLayout都比较简单,onLayout也只是去将我们的menu设置到屏幕的左侧以至于不可见。onInterceptTouchEvent和onTouchEvent中都只需要简单的调用mDragHelper的方法。那么核心代码都在我们的ViewDragHelper.Callback的实例中了。注意一点:我们LeftDrawerLayout默认的LayoutParams为MarginLayoutParams,注意复写相关方法。
接下来就把注意力放到我们的Callback中,我尽可能的按照调用的逻辑来解释各个方法:
onEdgeDragStarted 如果你仔细的看过上篇,那么一定知道这个方法的回调位置;因为我们的View不可见,所以我们没有办法直接通过触摸到它来把menu设置为captureView,所以我们只能设置边界检测,当MOVE时回调onEdgeDragStarted时,我们直接通过captureChildView手动捕获。
tryCaptureView 那么既然我们手动捕获了,为什么还需要这个方法呢?主要是因为当Drawer拉出的时候,我们通过拖拽Drawer也能进行移动菜单。
clampViewPositionHorizontal 主要是移动的时候去控制控制范围,可以看到我们的
int newLeft = Math.max(-child.getWidth(), Math.min(left, 0));一定>=-child.getWidth() && <=0 。getViewHorizontalDragRange 为什么要复写,我们上篇已经描述过,返回captureView的移动范围。
onViewReleased 则是释放的时候触发的,我们计算当前显示的百分比,以及加速度来决定是否显示drawer,从代码可以看出,当
xvel > 0||xvel == 0 && offset > 0.5f显示我们的菜单,其他情况隐藏。这里注意一点xvel的值只有大于我们设置的minVelocity才会出现大于0,如果小于我们设置的值则一直是0。onViewPositionChanged 整个pos变化的过程中,我们计算offset保存,这里可以使用接口将offset回调出去,方便做动画。
ok,我们重写的所有方法描述完成了~那么博文主要内容也就结束了~哈~是不是 so easy ~!
呃,忘了MainActivity和Fragment的代码了~~按顺序贴
(三)LeftDrawerLayoutActivity
package com.imooc.testandroid;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import com.zhy.learn.view.LeftDrawerLayout;
public class LeftDrawerLayoutActivity extends ActionBarActivity
{
private LeftMenuFragment mMenuFragment;
private LeftDrawerLayout mLeftDrawerLayout ;
private TextView mContentTv ;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_left_drawer_layout);
mLeftDrawerLayout = (LeftDrawerLayout) findViewById(R.id.id_drawerlayout);
mContentTv = (TextView) findViewById(R.id.id_content_tv);
FragmentManager fm = getSupportFragmentManager();
mMenuFragment = (LeftMenuFragment) fm.findFragmentById(R.id.id_container_menu);
if (mMenuFragment == null)
{
fm.beginTransaction().add(R.id.id_container_menu, mMenuFragment = new LeftMenuFragment()).commit();
}
mMenuFragment.setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener()
{
@Override
public void menuItemSelected(String title)
{
mLeftDrawerLayout.closeDrawer();
mContentTv.setText(title);
}
});
}
可以看到drawer使用了一个Fragment ~~
(四) LeftMenuFragment
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;
/**
* Created by zhy on 15/4/26.
*/
public class LeftMenuFragment extends ListFragment {
private static final int SIZE_MENU_ITEM = 3;
private MenuItem[] mItems = new MenuItem[SIZE_MENU_ITEM];
private LeftMenuAdapter mAdapter;
private LayoutInflater mInflater;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = LayoutInflater.from(getActivity());
MenuItem menuItem = null;
for (int i = 0; i < SIZE_MENU_ITEM; i++) {
menuItem = new MenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light);
mItems[i] = menuItem;
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setBackgroundColor(0xffffffff);
setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if (mMenuItemSelectedListener != null) {
mMenuItemSelectedListener.menuItemSelected(((MenuItem) getListAdapter().getItem(position)).text);
}
mAdapter.setSelected(position);
}
//选择回调的接口
public interface OnMenuItemSelectedListener {
void menuItemSelected(String title);
}
private OnMenuItemSelectedListener mMenuItemSelectedListener;
public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener menuItemSelectedListener) {
this.mMenuItemSelectedListener = menuItemSelectedListener;
}
}
public class MenuItem
{
public MenuItem(String text, boolean isSelected, int icon, int iconSelected) {
this.text = text;
this.isSelected = isSelected;
this.icon = icon;
this.iconSelected = iconSelected;
}
boolean isSelected;
String text;
int icon;
int iconSelected;
}
/**
* Created by zhy on 15/4/26.
*/
public class LeftMenuAdapter extends ArrayAdapter<MenuItem> {
private LayoutInflater mInflater;
private int mSelected;
public LeftMenuAdapter(Context context, MenuItem[] objects) {
super(context, -1, objects);
mInflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
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(getItem(position).text);
iv.setImageResource(getItem(position).icon);
convertView.setBackgroundColor(Color.TRANSPARENT);
if (position == mSelected) {
iv.setImageResource(getItem(position).iconSelected);
convertView.setBackgroundColor(getContext().getResources().getColor(R.color.state_menu_item_selected));
}
return convertView;
}
public void setSelected(int position) {
this.mSelected = position;
notifyDataSetChanged();
}
}
贴了一大串,主要就是Fragment的代码,内部有个ListView,所以还有个Adapter,这个Fragment在之前的博文中出现过,搬过来而已。item的布局文件就是一个TextView和ImageView~~不贴了~~大家自己下载源码~
源码点击下载(导入方式注意看下ReadMe)
ok,到此结束~~hava a nice day ~~
欢迎关注我的微博:
http://weibo.com/u/3165018720
群号:463081660,欢迎入群
微信公众号:hongyangAndroid
(欢迎关注,第一时间推送博文信息)
版权声明:本文为博主原创文章,未经博主允许不得转载。
ViewDragHelper实战 自己打造Drawerlayout的更多相关文章
- Go项目实战:打造高并发日志采集系统(六)
前情回顾 前文我们完成了日志采集系统的日志文件监控,配置文件热更新,协程异常检测和保活机制. 本节目标 本节加入kafka消息队列,kafka前文也介绍过了,可以对消息进行排队,解耦合和流量控制的作用 ...
- Go项目实战:打造高并发日志采集系统(五)
前情回顾 前文我们完成了如下功能1 根据配置文件启动多个协程监控日志,并启动协程监听配置文件.2 根据配置文件热更新,动态协调日志监控.3 编写测试代码,向文件中不断写入日志并备份日志,验证系统健壮性 ...
- Go项目实战:打造高并发日志采集系统(四)
前情回顾 前文我们完成了如下目标1 项目架构整体编写2 使框架支持热更新 本节目标 在前文的框架基础上,我们1 将之前实现的日志监控功能整合到框架中.2 一个日志对应一个监控协程,当配置热更新后根据新 ...
- Go项目实战:打造高并发日志采集系统(三)
前文中已经完成了文件的监控,kafka信息读写,今天主要完成配置文件的读写以及热更新.并且规划一下系统的整体结构,然后将之前的功能串起来形成一套完整的日志采集系统. 前情提要 上一节我们完成了如下目标 ...
- Go项目实战:打造高并发日志采集系统(二)
日志统计系统的整体思路就是监控各个文件夹下的日志,实时获取日志写入内容并写入kafka队列,写入kafka队列可以在高并发时排队,而且达到了逻辑解耦合的目的.然后从kafka队列中读出数据,根据实际需 ...
- Go项目实战:打造高并发日志采集系统(一)
项目结构 本系列文章意在记录如何搭建一个高可用的日志采集系统,实际项目中会有多个日志文件分布在服务器各个文件夹,这些日志记录了不同的功能.随着业务的增多,日志文件也再增多,企业中常常需要实现一个独立的 ...
- [转]Android自定义控件三部曲系列完全解析(动画, 绘图, 自定义View)
来源:http://blog.csdn.net/harvic880925/article/details/50995268 一.自定义控件三部曲之动画篇 1.<自定义控件三部曲之动画篇(一)—— ...
- 自定义View其实很简单系列1-12
作者: AigeStudio http://blog.csdn.net/aigestudio 说明:文中的1/12表示12篇中的第1篇, 1/6=2/12表示12篇中的第2篇,其它类似. 自定义控件 ...
- Android ViewDragHelper及移动处理总结
概述 2013年谷歌i/o大会上介绍了两个新的layout: SlidingPaneLayout和DrawerLayout,现在这俩个类被广泛的运用.我们知道在我们实际的开发中往往会涉及到很多的拖动效 ...
随机推荐
- Activity之间传递大数据问题
Android开发人员都知道,Intent适用于在不同的Activity之间传递数据,包括参数.字符串.以及序列化的对象等.但是笔者所做的项目用到了使用Intent 传递Bitmap图片对象,图片的数 ...
- 初探linux子系统集之写在前言
毕业两周年,进入嵌入式linux这个行业也已两个年头有余,从开始的linux驱动,android的framework,到现在的linux应用,android的app以及产品的零零总总,其实很想把这些都 ...
- partition List(划分链表)
Given a linked list and a value x, partition it such that all nodes less than x come before nodes gr ...
- 简单工厂,Factory Method(工厂方法)和Abstract Factory(抽象工厂)模式
对于简单工厂来说,它的工厂只能是这个样子的 public class SimplyFactory { /** * 静态工厂方法 */ public static Prouct factory(Str ...
- Charles手机抓包实用教程
一.Charles官网下载链接:https://www.charlesproxy.com/download/ 二.抓包步骤: 1.安装Charles,并打开 2.电脑设置代理端口:打开charles- ...
- Spring MVC “404 Not Found”错误的解决
一般这个问题发生的原因不会出在Web.xml配置文件的Servlet URL映射部分,因为这个URL映射做JavaWeb的人经常配置不会发生错误,而且此处的这个映射自由度很大,可以有目录字符串,也可以 ...
- ASP.NET MVC中的路由IRouteConstraint方法应用实例
在如下代码的写法中: public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { ro ...
- var与let、const的区别
var与let.const 一.var声明的变量会挂载在window上,而let和const声明的变量不会: var a = 100;console.log(a,window.a); // 100 1 ...
- spring boot入门篇,helloworld案例演示
为什么用spring boot? 嵌入的 Tomcat,无需部署 WAR 文件 简化 Maven 配置 无需 XML 配置,轻松快速地搭建Spring Web应用 开始学习SpringBoot 构建简 ...
- Flask构建微电影(二)
第三章.项目分析.搭建目录及模型设计 3.1.前后台项目目录分析 微电影网站 前台模块 后台模块 前台(home) 数据模型:models.py 表单处理:home/forms.py 模板目录:tem ...
