侧滑面板(对ViewGroup的自定义)

应用场景: 扩展主面板的功能

功能实现:1.ViewDragHelper: Google2013年IO大会提出的,>解决界面控件拖拽移动问题. (v4包下)

2.mTouchSlop 最小敏感范围, 值越小, 越敏感

伴随动画: 1. 左面板: 缩放动画, 平移动画, 透明度动画

2.主面板: 缩放动画

3.背景动画: 亮度变化 (颜色变化)

状态监听\触摸优化:1.设置并更新状态

2.触摸优化: 重写ViewGroup里onInterceptTouchEvent和onTouchEvent


1.下面是自定义ViewGroup的类 Draglayout(整个demo最主要的地方)

public class Draglayout extends FrameLayout {private ViewDragHelper drag;private ViewGroup mLeftContent;private ViewGroup mMainContent;private int mrange;private int measuredHeight;private int measuredWidth;private Status status = Status.Close;// 默认状态public Draglayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public Draglayout(Context context, AttributeSet attrs) {super(context, attrs);init();}public Draglayout(Context context) {super(context);init();}// 状态枚举public static enum Status {Close, Open, Draging;}// 定义接口,当开关状态改变的时候执行特定的方法private onDragStatusChangeListener listener;public void setDragStatusChangeListener(onDragStatusChangeListener mListener) {this.listener = mListener;}public interface onDragStatusChangeListener {void onClose();// 当关闭时void onOpen();// 当打开时void onDraging(float percent);// 当在滑动时}public Status getStatus() {return status;}public void setStatus(Status status) {this.status = status;}private void init() {// 1.初始化(谷歌大会13推出解决界面拖拽问题)drag = ViewDragHelper.create(this, callback);}ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {// 3.重写方法@Overridepublic boolean tryCaptureView(View child, int pointerId) {// 1. 根据返回结果决定当前child是否可以拖拽// child 当前被拖拽的View// pointerId 区分多点触摸的idLog.d("tencent", "child" + child);return true;}public void onViewCaptured(View capturedChild, int activePointerId) {// 当被捕获时调用Log.d("tencent", "capturedChild" + capturedChild);};public int getViewHorizontalDragRange(View child) {// 返回拖拽的范围, 不对拖拽进行真正的限制. 仅仅决定了动画执行速度,通过这个范围计算出速度return mrange;};// 就这么定义public int clampViewPositionHorizontal(View child, int left, int dx) {// 根据建议值 修正将要移动到的(横向)位置 (重要)// 此时没有发生真正的移动// child: 当前拖拽的View// left 新的位置的建议值, dx 位置变化量// left = oldLeft + dx;//主页面显示的位置if (child == mMainContent) {left = fixLeft(left);}return left;};// public int clampViewPositionVertical(View child, int top, int dy) {// return top;// };// 位置改变的时候public void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {// 当View位置改变的时候, 处理要做的事情 (更新状态, 伴随动画, 重绘界面)// 此时,View已经发生了位置的改变int newLeft = left;// 滑动侧边页,带动主页面滑动if (changedView == mLeftContent) {newLeft = mMainContent.getLeft() + dx;}newLeft = fixLeft(newLeft);// 修正if (changedView == mLeftContent) {//强制让左面板回到原始位置mLeftContent.layout(0, 0, measuredWidth, measuredHeight);mMainContent.layout(newLeft, 0, newLeft + measuredWidth,measuredHeight);}// 设置伴随动画dispatchDragEvent(newLeft);invalidate();// 为了兼容低版本,保存设置};// 松手的时候设置页面的位置public void onViewReleased(View releasedChild, float xvel, float yvel) {// View releasedChild 被释放的子View// float xvel 水平方向的速度, 向右为+// float yvel 竖直方向的速度, 向下为+if (xvel == 0 && mMainContent.getLeft() > mrange / 2.0f) {open();} else if (xvel > 0) {open();} else {close();}}};// 设置限制位置protected int fixLeft(int left) {if (left < 0)return 0;if (left > mrange)return mrange;return left;}/** 状态改变设置动画 */protected void dispatchDragEvent(int newLeft) {// 状态改变的时候设置动画百分比float percent = newLeft / (1.0f * mrange);// 伴随动画animationViews(percent);// 设置监听执行if (listener != null) {listener.onDraging(percent);}//记录状态Status preStatus = status;//根据percent 状态发生改变if (percent == 0f) {status = Status.Close;} else if (percent == 1.0f) {status = Status.Open;} else {status = Status.Draging;}//如果不等于上次的状态,发生了变化if (status != preStatus) {if (status == Status.Close) {if (listener != null)listener.onClose();} else if (status == Status.Open) {if (listener != null)listener.onOpen();}}}private void animationViews(float percent) {// * 伴随动画:// > 1. 左面板: 缩放动画, 平移动画, 透明度动画// > 2. 主面板: 缩放动画// > 3. 背景动画: 亮度变化 (颜色变化)// > 1. 左面板: 缩放动画, 平移动画, 透明度动画// 缩放动画 从0.5--》1// mLeftContent.setScaleX(0.5f*percent+0.5f);// mLeftContent.setScaleY(0.5f*percent+0.5f);ViewHelper.setScaleX(mLeftContent, 0.5f * percent + 0.5f);// 为了兼容低版本,9个低版本兼容动画ViewHelper.setScaleY(mLeftContent, evaluate(percent, 0.5f, 1.0f));// 平移动画 从 -measuredWidth/2.0f---> 0.0fViewHelper.setTranslationX(mLeftContent,evaluate(percent, -measuredWidth / 2.0f, 0.0f));// 透明度 从0.5f-->1.0fViewHelper.setAlpha(mLeftContent, evaluate(percent, 0.5f, 1.0f));// > 2. 主面板: 缩放动画// 1.0f -> 0.8fViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f));ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f));// > 3. 背景动画: 亮度变化 (颜色变化)getBackground().setColorFilter((Integer) evaluateColor(percent, Color.BLACK,Color.TRANSPARENT), Mode.SRC_OVER);}// 数值估值器public Float evaluate(float fraction, Number startValue, Number endValue) {float startFloat = startValue.floatValue();return startFloat + fraction * (endValue.floatValue() - startFloat);}// 颜色估值器public Object evaluateColor(float fraction, Object startValue,Object endValue) {int startInt = (Integer) startValue;int startA = (startInt >> 24) & 0xff;int startR = (startInt >> 16) & 0xff;int startG = (startInt >> 8) & 0xff;int startB = startInt & 0xff;int endInt = (Integer) endValue;int endA = (endInt >> 24) & 0xff;int endR = (endInt >> 16) & 0xff;int endG = (endInt >> 8) & 0xff;int endB = endInt & 0xff;return (int) ((startA + (int) (fraction * (endA - startA))) << 24)| (int) ((startR + (int) (fraction * (endR - startR))) << 16)| (int) ((startG + (int) (fraction * (endG - startG))) << 8)| (int) ((startB + (int) (fraction * (endB - startB))));}protected void open() {open(true);}protected void close() {close(true);}@Overridepublic void computeScroll() {super.computeScroll();// 持续平滑动画 (高频率调用)if (drag.continueSettling(true)) {ViewCompat.postInvalidateOnAnimation(this);}}private void open(boolean issmooth) {int finalleft = mrange;if (issmooth) {if (drag.smoothSlideViewTo(mMainContent, finalleft, 0)) {// 左和上的位置ViewCompat.postInvalidateOnAnimation(this);}} else {mMainContent.layout(finalleft, 0, measuredWidth + finalleft,measuredHeight);}}private void close(boolean issmooth) {int finalleft = 0;// 判断是否需要设置平滑if (issmooth) {if (drag.smoothSlideViewTo(mMainContent, finalleft, 0)) {ViewCompat.postInvalidateOnAnimation(this);}} else {mMainContent.layout(finalleft, 0, measuredWidth + finalleft,measuredHeight);}}// 2.传递触摸事件@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return drag.shouldInterceptTouchEvent(ev);}// public boolean dispatchTouchEvent(android.view.MotionEvent ev) {// return true;// };@Overridepublic boolean onTouchEvent(MotionEvent event) {try {drag.processTouchEvent(event);// 传递给drag} catch (Exception e) {e.printStackTrace();}// 返回true,持续接收事件return true;}// 获得自定义控件里面的子view@Overrideprotected void onFinishInflate() {super.onFinishInflate();// Github// 写注释// 容错性检查 (至少有俩子View, 子View必须是ViewGroup的子类)if (getChildCount() < 2) {throw new IllegalStateException("布局至少有俩孩子. Your ViewGroup must have 2 children at least.");}if (!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)) {throw new IllegalArgumentException("子View必须是ViewGroup的子类. Your children must be an instance of ViewGroup");}mLeftContent = (ViewGroup) getChildAt(0);mMainContent = (ViewGroup) getChildAt(1);}// 获得测量宽高@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);measuredHeight = getMeasuredHeight();measuredWidth = getMeasuredWidth();mrange = (int) (measuredWidth * 0.6f);}}

2.MainActivity类(填充数据,设置监听)

public class MainActivity extends Activity {private Draglayout draglayout;private ListView leftLV;private ListView mainLV;private ImageView iv_header;private MyLinearlayout myLinearlayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);initView();initData();}private void initView() {draglayout = (Draglayout) findViewById(R.id.dl);leftLV = (ListView) findViewById(R.id.lv_left);mainLV = (ListView) findViewById(R.id.lv_main);iv_header = (ImageView) findViewById(R.id.iv_header);myLinearlayout = (MyLinearlayout) findViewById(R.id.mainLL);}private void initData() {// 监听draglayout.setDragStatusChangeListener(new onDragStatusChangeListener() {@Overridepublic void onOpen() {Utils.showToast(MainActivity.this, "打开");Random random = new Random();int position = random.nextInt(20);leftLV.smoothScrollToPosition(position);// 随机位置}@Overridepublic void onDraging(float percent) {// iv_header.setAlpha(1-percent);//设置图标的透明度ViewHelper.setAlpha(iv_header, 1 - percent);}@Overridepublic void onClose() {Utils.showToast(MainActivity.this, "关闭");// 设置图标的震动// 图标反复移动的偏移ObjectAnimator ofFloat = ObjectAnimator.ofFloat(iv_header, "translationX", 10.0f);ofFloat.setInterpolator(new CycleInterpolator(1));// 来回一次ofFloat.setDuration(200);ofFloat.start();}});myLinearlayout.setDraglayout(draglayout);// 设置适配器leftLV.setAdapter(new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings) {@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = super.getView(position, convertView, parent);TextView textView = (TextView) view;textView.setTextColor(Color.WHITE);return textView;}});mainLV.setAdapter(new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1, Cheeses.NAMES));}
}

4.数据

public class Cheeses {public static final String[] sCheeseStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi","Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale","Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese","Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell","Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc","Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss","Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon","Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase","Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese","Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy","Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille","Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore","Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)","Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves","Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur","Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon","Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin","Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)","Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine","Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza","Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)","Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta","Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie","Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat","Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano","Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain","Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou","Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar","Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno","Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack","Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper","Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)","Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese","Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza"};public static final String[] NAMES = new String[] { "宋江", "卢俊义", "吴用","公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深","武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘","雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍"," 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪","魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方","郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充","李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿","陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩","周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立","李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜","时迁", "段景柱" };}

5.utils类

public class Utils {public static Toast mToast;public static void showToast(Context mContext, String msg) {if (mToast == null) {mToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT);}mToast.setText(msg);mToast.show();}/*** dip 转换成 px* @param dip* @param context* @return*/public static float dip2Dimension(float dip, Context context) {DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);}/*** @param dip* @param context* @param complexUnit {@link TypedValue#COMPLEX_UNIT_DIP} {@link TypedValue#COMPLEX_UNIT_SP}}* @return*/public static float toDimension(float dip, Context context, int complexUnit) {DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();return TypedValue.applyDimension(complexUnit, dip, displayMetrics);}/** 获取状态栏高度* @param v* @return*/public static int getStatusBarHeight(View v) {if (v == null) {return 0;}Rect frame = new Rect();v.getWindowVisibleDisplayFrame(frame);return frame.top;}public static String getActionName(MotionEvent event) {String action = "unknow";switch (MotionEventCompat.getActionMasked(event)) {case MotionEvent.ACTION_DOWN:action = "ACTION_DOWN";break;case MotionEvent.ACTION_MOVE:action = "ACTION_MOVE";break;case MotionEvent.ACTION_UP:action = "ACTION_UP";break;case MotionEvent.ACTION_CANCEL:action = "ACTION_CANCEL";break;case MotionEvent.ACTION_SCROLL:action = "ACTION_SCROLL";break;case MotionEvent.ACTION_OUTSIDE:action = "ACTION_SCROLL";break;default:break;}return action;}
}

5.MyLinearlayout类,这个类是对打开状态下,拦截子listView中的滑动事件

public class MyLinearlayout extends LinearLayout{private Draglayout draglayout;public MyLinearlayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public MyLinearlayout(Context context, AttributeSet attrs) {super(context, attrs);}public MyLinearlayout(Context context) {super(context);}public void setDraglayout(Draglayout draglayout){this.draglayout = draglayout;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {//如果不是关闭状态,则拦截掉,不让其处理滑动事件,否则原先处理if(( draglayout).getStatus()==Status.Close){return super.onInterceptTouchEvent(ev);}else{return true;}}public boolean onTouchEvent(MotionEvent event) {//如果是关闭状态,原先处理if(( draglayout).getStatus()==Status.Close){return super.onTouchEvent(event);}else{//否则当手指抬起的时候就关闭if(event.getAction()==MotionEvent.ACTION_UP){draglayout.close();}return true;}}
}


1.仿QQ侧滑面板(对ViewGroup的自定义)相关推荐

  1. 仿QQ侧滑面板(三)

    1.5 状态更新及事件回调 1.5.1 状态分析 1.5.2 事件回调分析 1.5.2 实现状态更新及事件回调 1.6 触摸优化 1.6.1 填充界面数据 1.6.2 主面板触摸优化 1.5 状态更新 ...

  2. 仿QQ侧滑面板(二)

    1.3 结束动画 1.3.1 跳转的结束动画 1.3.2 平滑的结束动画 1.4 伴随动画 1.4.1 分解伴随动画 1.4.2 实现伴随动画 1.3 结束动画 拖拽过程中当手指抬起时,需要实现一个打 ...

  3. Android仿QQ侧滑菜单

    先上效果图: GIF图有点模糊,源码已上传Github:Android仿QQ侧滑菜单 ####整体思路: 自定义ItemView的根布局(SwipeMenuLayout extends LinearL ...

  4. 名片夹android布局代码,Android自定义布局实现仿qq侧滑部分代码

    自定义布局实现仿qq侧滑部分Android代码,供大家参考,具体内容如下 实现说明: 通过自定义布局实现: SlidingLayout继承于 HorizontalScrollView /** * Cr ...

  5. Android高仿QQ侧滑菜单

    文章目录 效果图 整体思路 实现过程 先分析SwipeMenuLayout 再分析下SwipeRecycleView 踩过的坑 后记 效果图 GIF图有点模糊,源码已上传Github:Android仿 ...

  6. android仿qq布局,Android自定义布局实现仿qq侧滑部分代码

    自定义布局实现仿qq侧滑部分android代码,供大家参考,具体内容如下 实现说明: 通过自定义布局实现: slidinglayout继承于 horizontalscrollview /** * cr ...

  7. Android 仿QQ侧滑菜单

    前言 集成方式 兼容超强的BaseRecyclerViewAdapterHelper 方法及属性介绍 THANKS 侧滑的雏形 测绘布局 onLayout onMeasure MotionEvent事 ...

  8. Android学习之仿QQ侧滑功能的实现

    现在项目越来越多的应用了滑动删除的功能,Android本来遵循的是长按删除,IOS定制的是滑动删除,不可否认滑动删除确实在客户体验上要好一点,所以看了很多关于仿QQ滑动删除的例子,还是感觉代码家的An ...

  9. 鹅厂系列一 : 仿QQ侧滑菜单

    --不会的东西你不尝试的去做,你永远都不会做 好了,跟随潮流,还是先看下效果,不然可能都没人想看下去了(不会看到效果后不想看了吧O(∩_∩)O~) 额,图片资源来自QQ_374.APK,里面四五千个图 ...

最新文章

  1. 和远程ip_漏洞Microsoft Windows TCP/IP 远程执行代码漏洞威胁通告
  2. java什么时候用有参_Java有陷阱——慎用入参做返回值
  3. Matlab | Matlab从入门到放弃(10)——线性方程组
  4. c语言多线编程程序,C语言之多线编程 来看看?
  5. Android 微信登录
  6. 谈谈怎样提高炼丹手速
  7. DeOccNet:国防科大提出阵列相机去除前景遮挡成像新方法
  8. 6 功能4:文章详情页、点赞功能
  9. 线性反馈移位寄存器(LFSR,Linear Feedback Shift Register)
  10. 使用Navicat导入备份的数据库文件操作流程
  11. 产品经理工具之软件篇
  12. sinc函数卷积_两个矩形窗函数的卷积
  13. 最基本的计算机度量单位是什么意思,计算机常用的度量单位
  14. 2018-2019-2 20165205《网络对抗技术》Exp4 恶意代码分析
  15. ui设计需要做android和苹果版本,安卓和IOS系统对于UI设计来说一样吗
  16. 浅谈幼儿园计算机论文,浅谈幼儿园科学教育活动中,计算机的辅助教学
  17. 查看计算机ip地址配置信息的命令是,查看电脑配置指令大全
  18. 服务器被攻击怎么办?如何防止服务器被攻击?
  19. 使用 Traci进行仿真
  20. 六款好用的视频编辑软件推荐

热门文章

  1. vs2010c语言命令行参数,模仿Visual Studio————命令行编译C/C++程序
  2. 一种处理亿级聚合数据的方法
  3. 3.网络,web服务器——udp
  4. c语言STUP 200,STUP08C中文资料
  5. 扩频系列matlab,matlab扩频通信
  6. 2015华为实习生招聘机试、面试记录
  7. 经典问题之乐观锁和悲观锁及使用场景
  8. ASEMI三相整流桥D50XT80如何检测
  9. opencv将图片组合成视频播放
  10. Rational Rose画序列图