Android 固定式底部上滑抽屉view

1、自定义view  BottomDrawerLayout

/*** 作者:created by meixi* 邮箱:15913707499@163.com* 日期:2018/12/10 11*/public class BottomDrawerLayout extends ViewGroup {private static final String TAG = "BottomDrawerLayout";private View mDrawerView;private View mBottomView;private View mRotateView;private ViewDragHelper mDragHelper;private float mInitialX;private float mInitialY;private int mTouchSlop;private int mCurTop=-1;private int mBottomHeight;private int mDrawerHeight;private int mParentHeight;private float mDragOffset = 1;private boolean isUnderBottomView = false;private boolean isUnderDrawerView = false;private OnDrawerStatusChanged onDrawerStatusChanged;public BottomDrawerLayout(Context context) {super(context);init(context);}public BottomDrawerLayout(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public BottomDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {mDragHelper = ViewDragHelper.create(this, 1.0f,new DragerCallBack());mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();}public void setOnDrawerStatusChanged(OnDrawerStatusChanged onDrawerStatusChanged) {this.onDrawerStatusChanged = onDrawerStatusChanged;}public void switchDrawer() {if(mDragOffset<1){minimize();}else{maximize();}}private class DragerCallBack extends  ViewDragHelper.Callback{//从底部到顶部的顺序遍历子view@Overridepublic int getOrderedChildIndex(int index) {int childCount = BottomDrawerLayout.this.getChildCount();int newIndex = childCount - index -1;return  newIndex;}@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child == mDrawerView;}@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {
//            Log.d(TAG, "clampViewPositionHorizontal " + left + "," + dx);
//            final int leftBound  = getPaddingLeft();
//            final int rightBound = getWidth() - mBottomView.getWidth() - leftBound;
//            //坐标系三种情况
//            final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
//
//            return newLeft;return super.clampViewPositionHorizontal(child, left, dx);}//要想上下拖动必须重写此方法@Overridepublic int clampViewPositionVertical(View child, int top, int dy) {
//            Log.d(TAG, "clampViewPositionVertical " + top + "," + dy);final int topBound = getHeight() - mDrawerView.getMeasuredHeight() - mBottomView.getHeight();final int bottomBound = getHeight()  - mBottomView.getHeight();final int newTop = Math.min(Math.max(top, topBound), bottomBound);return newTop;}@Overridepublic void onViewCaptured(View capturedChild, int activePointerId) {super.onViewCaptured(capturedChild, activePointerId);}@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {//            Log.i(TAG, "onViewReleased:" + "xvel:" + xvel + ",yvel:" + yvel);//yvel Fling产生的值,yvel > 0 则是快速往下Fling || yvel < 0 则是快速往上Flingint top = mParentHeight - mDrawerHeight - mBottomHeight;if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)/* 后面这个小括号里判断处理拖动之后停下来但是未松手的情况 */) {top += mDrawerHeight;}mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);invalidate();//important 不加,就不会刷新View的位置}@Overridepublic void onViewDragStateChanged(int state) {super.onViewDragStateChanged(state);}@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {mCurTop = top;mDragOffset = ((float) top -(mParentHeight - mDrawerHeight - mBottomHeight))/ mDrawerHeight;
//            Log.d(TAG, "onViewPositionChanged: mDragOffset:" + mDragOffset);//旋转与透明跟随效果mDrawerView.setAlpha(1-mDragOffset);
//            mRotateView.setRotation((1-mDragOffset)*180);requestLayout();if (onDrawerStatusChanged != null) {onDrawerStatusChanged.onChanged(mParentHeight,top);}//            if(onDrawerStatusChanged !=null){
//                if(mDragOffset == 0 || mDragOffset == 1){
//                    onDrawerStatusChanged.onChanged(mParentHeight,top);
//                }
//            }}}public interface OnDrawerStatusChanged{void onChanged(int parentHeight, int drawerTop);}public void maximize(){smoothSlideTo(0.0f);}public void minimize(){smoothSlideTo(1.0f);}private boolean smoothSlideTo(float slideOffset) {final int topBound = mParentHeight - mDrawerHeight - mBottomHeight;int y = (int) (topBound + slideOffset * mDrawerHeight);if(mDragHelper.smoothSlideViewTo(mDrawerView, mDrawerView.getLeft(), y)){ViewCompat.postInvalidateOnAnimation(this);return true;}return false;}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {int act = MotionEventCompat.getActionMasked(event);
//        final int action = event.getAction();switch (act) {//由于很多情况不能拦截事件,这种时候系统不会调用onTouchEvent()// 手动把事件传递给mDragHelper.processTouchEventcase MotionEvent.ACTION_DOWN:mInitialX = event.getX();mInitialY = event.getY();//Feed the down event to the detector so it has// context when/if dragging begins
//                mDetector.onTouchEvent(event);mDragHelper.processTouchEvent(event);isUnderBottomView = mDragHelper.isViewUnder(mBottomView, (int)mInitialX, (int)mInitialY);isUnderDrawerView = mDragHelper.isViewUnder(mDrawerView, (int)mInitialX, (int)mInitialY);break;case MotionEvent.ACTION_POINTER_DOWN:mDragHelper.processTouchEvent(event) ;break;case MotionEvent.ACTION_POINTER_UP:mDragHelper.processTouchEvent(event) ;break;case MotionEvent.ACTION_MOVE:final float x = event.getX();final float y = event.getY();final int yDiff = (int) Math.abs(y - mInitialY);final int xDiff = (int) Math.abs(x - mInitialX);//Verify that either difference is enough to be a dragif ((yDiff > mTouchSlop || xDiff > mTouchSlop) && (isUnderBottomView || isUnderDrawerView) ){//Start capturing eventsreturn true;}break;}//父类是viewgroup,返回的falsereturn super.onInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {mDragHelper.processTouchEvent(event);//down事件返回false,让其底部的平行层级的view能够接收到点击事件switch (event.getAction()) {case MotionEvent.ACTION_DOWN:return false;case MotionEvent.ACTION_UP:return false;//只有当手指达到拖动阈值时this才确定消耗此系列事件//若未达到阈值也返回true,则与其平行的view不会收到click事件case MotionEvent.ACTION_MOVE:final float x = event.getX();final float y = event.getY();final int yDiff = (int) Math.abs(y - mInitialY);final int xDiff = (int) Math.abs(x - mInitialX);//Verify that either difference is enough to be a dragif ((yDiff > mTouchSlop || xDiff > mTouchSlop) && (isUnderBottomView || isUnderDrawerView) ){//Start capturing eventsreturn true;}break;}return false;}@Overrideprotected void onFinishInflate() {super.onFinishInflate();mBottomView = findViewById(R.id.layout_bottom_bar);mDrawerView = findViewById(R.id.layout_price_detail);//        mRotateView = findViewById(R.id.img_spread_out);mBottomView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {maximize();}});mDrawerView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {minimize();}});}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {measureChildren(widthMeasureSpec,heightMeasureSpec);super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mParentHeight = this.getHeight();mBottomHeight = mBottomView.getMeasuredHeight();mDrawerHeight = mDrawerView.getMeasuredHeight();
//        Log.d(TAG, "onLayout: drawHeight:"+drawHeight);mBottomView.layout(l,mParentHeight - mBottomHeight,r,b);if(mCurTop == -1){mCurTop = mParentHeight - mBottomHeight;}mDrawerView.layout(l,mCurTop,r,mCurTop + mDrawerHeight);}@Overridepublic void computeScroll() {if(mDragHelper.continueSettling(true)){ViewCompat.postInvalidateOnAnimation(this);}}
}

layout布局中使用这个自定义抽屉view即可,第一个LinearLayout是抽屉内容,第二个LinearLayout是固定底部把手

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="match_parent"><com.tianxin.shanghuact.BottomDrawerLayoutandroid:id="@+id/bottom_drawer_layout"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/layout_price_detail"android:layout_width="match_parent"android:background="@color/colorPrimary"android:orientation="horizontal"android:layout_height="180dp"></LinearLayout><LinearLayoutandroid:id="@+id/layout_bottom_bar"android:layout_width="match_parent"android:background="@color/transparent"android:orientation="horizontal"android:layout_height="40dp"></LinearLayout></com.tianxin.shanghuact.BottomDrawerLayout></RelativeLayout>

 抽屉demo连接:Android开发,抽屉viewdemo-Android文档类资源-CSDN下载

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/414907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端学习(2694):重读vue电商网站15之阻止页签tabs切换

主要函数如下&#xff1a; 在我们的 tabs 标签页添加一个 before-leave 函数 然后在 methods 中定义&#xff0c;根据第一个标签页的逻辑来阻止标签页的切换。

vscode中vue3项目vetur报错

vue3支持的插件应该是volar&#xff0c;之前有老的vue2项目插件vetur&#xff0c;所以会报错。 解决办法&#xff1a;在项目中新建.vscode, 新建文件settings.json,里面内容如下&#xff1a; {"vetur.validation.template": false,"vetur.validation.script&qu…

Android 侧滑多层view显示

侧滑module链接&#xff1a;https://download.csdn.net/download/meixi_android/10840271 引用方法 只需layout布局文件引用即可——第一个LinearLayout是底层view&#xff0c;第二个LinearLayout是上层view&#xff0c;侧滑即可显示底层view <com.daimajia.swipe.SwipeL…

MySQL的btree索引和hash索引的区别

MySQL的btree索引和hash索引的区别ash 索引结构的特殊性&#xff0c;其检索效率非常高&#xff0c;索引的检索可以一次定位&#xff0c;不像B-Tree 索引需要从根节点到枝节点&#xff0c;最后才能访问到页节点这样多次的IO访问&#xff0c;所以 Hash 索引的查询效率要远高于 B-…

vue2 vite antd vue 配置组件按需加载

1、安装vite-plugin-importer 2、配置vite.config.ts文件 import { fileURLToPath, URL } from url import { defineConfig } from vite import vue from vitejs/plugin-vue import vueJsx from vitejs/plugin-vue-jsx import usePluginImport from vite-plugin-importerexpo…

Android QQ登录集成

1、首先到腾讯开放平台创建应用&#xff0c;并上线——使用有效APP ID才可以进行qq登录 腾讯开放平台&#xff1a;https://open.tencent.com/ 上线后APP ID 2、下载腾讯sdk&#xff1a;https://download.csdn.net/download/meixi_android/10842092 3、activity代码详情&#…

前端学习(2695):重读vue电商网站16之Upload 上传组件

通过点击或者拖拽上传文件 Js <!-- action表示图片上传后台api地址 --> <el-upload:action"uploadURL":on-preview"handlePreview":on-remove"handleRemove"list-type"picture" ><el-button size"small" typ…

ajax 跨域

ajax跨域的原理 ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”,可以参考 浏览器同源政策及其规避方法 CORS请求原理 CORS是一个W3C标准&#xff0c;全称是"跨域资源共享"&#xff08;Cross-origin resource sharing&#xff09;。它允许浏览器向跨…

vue3使用process报错Uncaught ReferenceError: process is not defined

我习惯于在config中根据process判断打包状态。这次升级到vue3遇到报错。 解决方案&#xff0c;还是配置一下vite.config.json。 增加如下配置即可。 export default defineConfig({// ...define: {process.env: process.env} })

左右滑动实现activity之间的跳转

首先来看一下实现效果 1. BaseActivity extends Activity 首先&#xff0c;由于activity类之间存在很多共性 &#xff0c;比如跳以及滑动等事件&#xff0c;所以需要抽象出一个父类来&#xff0c;简化代码量。 附代码&#xff1a; /*** 按照1、2、3的步骤走* / public abstr…

vue3 watch props 监听属性变化

我的需求是弹出一个模态框。使用visible控制隐藏与现实&#xff0c;需要watch&#xff0c;visible变化&#xff0c;执行其他相关操作。 核心代码如下&#xff1a; import { watch, toRefs } from "vue"; const props defineProps({visible: {type: Boolean,default…

Android 调用原生API获取地理位置和经纬度,判断所在国家

public static boolean isCN(Context context) {TelephonyManager tm (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);String countryIso tm.getSimCountryIso();boolean isCN false;//判断是不是大陆if (!TextUtils.isEmpty(countryIso)) {count…

3 View - 状态保持 session

1.状态保持 http协议是无状态的&#xff1a;每次请求都是一次新的请求&#xff0c;不会记得之前通信的状态客户端与服务器端的一次通信&#xff0c;就是一次会话实现状态保持的方式&#xff1a;在客户端或服务器端存储与会话有关的数据存储方式包括cookie、session&#xff0c;…

拖拽升空的Android小火箭

先上演示效果 1、MainActivity 主布局就两个Button按钮 &#xff1a;一开启、二关闭 就不贴主布局xml了 因为小火箭是游离在activity之外的&#xff0c;所以不能依赖activity的生命周期 需要注意的一点是不要忘记在清单文件里配置 service 贴一下代码&#xff1a; public class…

vue3 vite ts 报错ReferenceError: React is not defined

解决方案&#xff1a; 1、安装vitejs/plugin-vue-jsx pnpm install vitejs/plugin-vue-jsx2、配置vite.config.ts import vueJsx from vitejs/plugin-vue-jsxexport default defineConfig({plugins: [vue(),vueJsx() ] })

Android view转bitmap,byte[]转Bitmap

1、自定义marker布局文件即自定义view文件 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height&…

yii验证系统学习记录,基于yiicms(一)写的太长了,再写一篇(二)

项目地址&#xff1a;https://gitee.com/templi/yiicms 感谢七觞酒大神的付出&#xff0c;和免费分享。当然也感谢yii2的开发团队们。 项目已经安全完毕&#xff0c;不知道后台密码&#xff0c;这种背景下&#xff0c;后台无法进去。绕不开的话题就是&#xff1a; 1.后台密码账…

前端学习(2697):重读vue电商网站18之监听图片删除事件

Js // 处理图片移除的操作 handleRemove(file) {// 1.获取将要删除的图片的临时路径const filePath file.response.data.tmp_path// 2.从pics数组中&#xff0c;找到这个图片对应的索引值const idx this.addForm.pics.findIndex(x > x.pic filePath)// 3.调用数组的 spli…

vscode开发java接口跳转到实现

我是mac系统&#xff0c;按照默认是commandF12&#xff0c;但是我的mac 13寸&#xff0c;按照这个快捷键&#xff0c;就显示亮度调节了。所以需要使用插件IntelliJ IDEA Keybindings来解决这个问题。 快捷方式如下&#xff1a;