自定义拉取菜单

//1.参数类:

public class Constant {

public static int POI_DEFAULT_SHOW_HEIGHT_MID = 1200;//单位dp

public final static int POI_DEFAULT_SHOW_HEIGHT_BOTTOM = 35;//单位dp

}

//2.自定义菜单

/**

* 可上下滑动的view

*

* @author dingys

* @description:

*/

public class CustomUpAndDownLayout extends RelativeLayout implements

OnTouchListener {

private Context mContext;

private int screenHeigh;

private VelocityTracker mVelocityTracker;

private int slideState;

private boolean isSliding;

private int moveMinValue = 20;// 单位dp,在做事件拦截时使用

private int defaultShowHeight = 200;// 单位dp

// 是否已经完全显示了

private boolean isFullVisible = false;

/*

* 滚动显示和隐藏上侧布局时,手指滑动需要达到的速度。

*/

public static final int SNAP_VELOCITY = 200;

 

/**

* 滑动状态 表示未进行任何滑动。

*/

public static final int DO_NOTHING = 0;

/**

* 滑动状态,表示正在往下滑动

*/

public static final int HIDING = 1;

 

/**

* 表示View 正在显示

*/

public static final int SHOWING = 3;

/**

* 滑动状态的一种,表示已经到达了底部时,继续向下滑动。

*/

public static final int HIDEING_2 = 4;

private int touchSlop;//在被判定为滚动之前用户手指可以移动的最大值。

private float xDown;

private float yDown;

private float xMove;

private float yMove;

private float yUp;

private LayoutParams contentLayoutParams;

private boolean once = false;

private int showMaxHeight;// 像素

private UpAndDownListener1 upAndDownListener;

private boolean isSlide = true;

 

public CustomUpAndDownLayout(Context context, AttributeSet attrs) {

super(context, attrs);

this.mContext = context;

init();

}

 

public CustomUpAndDownLayout(Context context) {

super(context);

this.mContext = context;

init();

}

 

public void setListener(UpAndDownListener1 upAndDownListener) {

this.upAndDownListener = upAndDownListener;

}

 

public void setDefaultShowHeight(int showHeight) {

this.defaultShowHeight = showHeight;

}

 

public void isOnce(boolean once, boolean isFull) {

this.once = once;

this.isFullVisible = isFull;

}

 

// 当底部bar显示或者隐藏时,重新设置this显示的最大高度

public void setMaxHeight(int h) {

showMaxHeight = h;

// 当是全屏时重新设置它的高度(情况有:底部状态栏显示隐藏,和横竖屏切换)

if (isFullVisible) {

if (contentLayoutParams != null) {

contentLayoutParams.height = h;

new Handler().postDelayed(new Runnable() {

@Override

public void run() {

CustomUpAndDownLayout.this

.setLayoutParams(contentLayoutParams);

}

}, 10);

}

}

 

}

 

public int getShowMaxH() {

return showMaxHeight;

}

 

 

int n = 0;

 

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if (!once) {

// 横竖屏切换时重新设置isFullVisible=false

if (contentLayoutParams != null) {// 重新设置高度

contentLayoutParams.height = Utils.dp2Px(mContext,

defaultShowHeight);

this.setLayoutParams(contentLayoutParams);

}

// 重新设置up and down icon

if (upAndDownListener != null) {

if (isFullVisible) {

upAndDownListener.onOpen();

} else {

upAndDownListener.onClose();

}

}

once = true;

}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

 

@Override

protected void onLayout(boolean changed, int arg1, int arg2, int arg3,

int arg4) {

contentLayoutParams = (LayoutParams) this.getLayoutParams();

super.onLayout(changed, arg1, arg2, arg3, arg4);

}

 

private void init() {

screenHeigh = Utils.getScreenHeight(mContext);

showMaxHeight = screenHeigh;

setOnTouchListener(this);

touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();

 

}

 

 

/**

* 设置是否允许滑动

*

* @param isSlide

*/

public void isSlide(boolean isSlide) {

this.isSlide = isSlide;

}

 

public boolean isCanSlide() {

return isSlide;

}

 

private boolean isScrolling = false;

public float downY;// 抬起时判断滑动距离时使用

 

@Override

public boolean onTouch(View view, MotionEvent event) {

if (!isSlide) {

return false;

}

createVelocityTracker(event);

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

// 手指按下时,记录按下时的坐标

xDown = event.getRawX();

yDown = event.getRawY();

downY = event.getRawY();

// 将滑动状态初始化为DO_NOTHING

slideState = DO_NOTHING;

 

// currTime = System.currentTimeMillis();

break;

case MotionEvent.ACTION_MOVE:

if (xDown == 0 && yDown == 0) {

xDown = event.getRawX();

yDown = event.getRawY();

downY = event.getRawY();

} else {

xMove = event.getRawX();

yMove = event.getRawY();

int moveDistanceX = (int) (xMove - xDown);

int moveDistanceY = (int) (yMove - yDown);

// 当默认是要显示全屏时,这个地方获取到的高度是-1(原因不清楚)

if (contentLayoutParams.height == -1) {

contentLayoutParams.height = showMaxHeight;

}

int tempheight = contentLayoutParams.height;// 得当前Layout距离顶部的距离

// 当布局已经到达底部或者已经到达顶部时,移动无效

if ((tempheight >= showMaxHeight && moveDistanceY < 0)

|| (tempheight <= Utils.dp2Px(mContext,

defaultShowHeight) && moveDistanceY > 0)) {

break;

}

// 检查当前的滑动状态

checkSlideState(moveDistanceX, moveDistanceY);

switch (slideState) {

case HIDING:

contentLayoutParams.height = showMaxHeight - moveDistanceY;

setLayoutParams(contentLayoutParams);

break;

case HIDEING_2:

case SHOWING:

int height = contentLayoutParams.height;

contentLayoutParams.height = height - moveDistanceY;

setLayoutParams(contentLayoutParams);

yDown = event.getRawY();

break;

default:

break;

}

}

break;

case MotionEvent.ACTION_UP:

yUp = event.getRawY();

if (isSliding) {

// 手指抬起时,进行判断当前手势的意图,这里可根据当前的位置判断显示还是隐藏

switch (slideState) {

case HIDING:

case HIDEING_2:

if (shouldScrollToUpMenu()) {

scrollToUpMenu();

} else {

scrollToContentFromUpMenu();

}

break;

case SHOWING:

if (shouldScrollToContentFromUpMenu()) {

scrollToContentFromUpMenu();

} else {

scrollToUpMenu();

}

break;

default:

break;

}

}

recycleVelocityTracker();

break;

}

return true;

}

 

/*

* 将界面从底部界面滚动到内容界面,滚动速度设定为30.

*/

public void scrollToContentFromUpMenu() {

new UpMenuScrollTask().execute(300);

}

 

/**

* 判断是否应该滚动将底部界面展示出来。如果手指移动距离大于屏幕宽度的1/4,或者手指移动速度大于SNAP_VELOCITY,

* 就认为应该滚动将底部界面展示出来。

*

* @return 如果应该将底部界面展示出来返回true,否则返回false。

*/

private boolean shouldScrollToUpMenu() {

return yUp - downY > 8;

}

 

/**

* 判断是否应该从底部界面滚动到内容布局,如果手指移动距离大于屏幕宽度的1/4,或者手指移动速度大于SNAP_VELOCITY,且必须是向上滚动的

* 就认为应该从底部界面滚动到内容布局。

*

* @return 如果应该从底部界面滚动到内容布局返回true,否则返回false。

*/

private boolean shouldScrollToContentFromUpMenu() {

float moveY = yUp - downY;

return (downY - yUp > screenHeigh / 4 || getScrollVelocity() > SNAP_VELOCITY)

&& moveY < 0;

}

 

/**

* 获取手指在绑定布局上的滑动速度。

*

* @return 滑动速度,以每秒钟移动了多少像素值为单位。

*/

private int getScrollVelocity() {

mVelocityTracker.computeCurrentVelocity(1000);

int velocity = (int) mVelocityTracker.getXVelocity();

return Math.abs(velocity);

}

 

/**

* 将界面滚动到底部界面界面,滚动速度设定为-30.

*/

public void scrollToUpMenu() {

new UpMenuScrollTask().execute(-300);

}

 

 

/**

* 检查滑动状态

*

* @param moveDistanceX

* @param moveDistanceY

*/

private void checkSlideState(int moveDistanceX, int moveDistanceY) {

if (!isFullVisible) {

// Y小于0 向上滑 Y 大于0 向下滑

if (!isSliding && Math.abs(moveDistanceY) >= touchSlop

&& moveDistanceY < 0) {

// 当在Y方向滑动的距离 大于手指滑动的最小距离 且向上滑动的时候

isSliding = true;

slideState = SHOWING;

} else if (!isSliding && Math.abs(moveDistanceY) >= touchSlop

&& moveDistanceY > 0) {

isSliding = true;

slideState = HIDEING_2;

}

} else {

// true是其全屏显示的时候,此时向上滑动不起作用,因此只在向下滑动做处理,此处少考虑了一种情况,当向下滑动之后,再

if (!isSliding && Math.abs(moveDistanceY) >= touchSlop

&& moveDistanceY > 0 && Math.abs(moveDistanceX) < touchSlop) {

isSliding = true;

slideState = HIDING;

contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 0);

contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);

setLayoutParams(contentLayoutParams);

}

}

}

 

private void createVelocityTracker(MotionEvent event) {

if (mVelocityTracker == null) {

mVelocityTracker = VelocityTracker.obtain();

}

mVelocityTracker.addMovement(event);

}

 

public boolean isShow() {

return isFullVisible;

}

 

/**

* 显示

*/

public void showView() {

new UpMenuScrollTask().execute(50);

}

 

/**

* 隐藏

*/

public void hideView() {

new UpMenuScrollTask().execute(-50);

}

 

/**

* 切换

*/

public void toggle() {

if (isFullVisible) {

hideView();

} else {

showView();

}

}

 

 

class UpMenuScrollTask extends AsyncTask<Integer, Integer, Integer> {

@Override

protected Integer doInBackground(Integer... speed) {

contentLayoutParams = (LayoutParams) getLayoutParams();

int height = contentLayoutParams.height;

// 根据传入的速度来滚动界面,当滚动到达边界值时,跳出循环。

while (true) {

height = height + speed[0];

if (height >= showMaxHeight) {

height = showMaxHeight;

break;

}

if (height <= Utils.dp2Px(mContext, defaultShowHeight)) {

height = Utils.dp2Px(mContext, defaultShowHeight);

break;

}

isScrolling = true;

publishProgress(height);

// 为了要有滚动效果产生,每次循环使线程睡眠一段时间,这样肉眼才能够看到滚动动画。

sleep(10);

}

if (speed[0] > 0) {

isFullVisible = true;

isScrolling = false;

 

} else {

isFullVisible = false;

 

}

slideState = DO_NOTHING;

isSliding = false;

return height;

}

 

@Override

protected void onProgressUpdate(Integer... height) {

contentLayoutParams.height = height[0];

setLayoutParams(contentLayoutParams);

unFocusBindView();

}

 

@Override

protected void onPostExecute(Integer height) {

contentLayoutParams.height = height;

setLayoutParams(contentLayoutParams);

if (upAndDownListener != null) {

if (isFullVisible) {

upAndDownListener.onOpen();

} else {

upAndDownListener.onClose();

}

}

}

}

 

private void unFocusBindView() {

setPressed(false);

setFocusable(false);

setFocusableInTouchMode(false);

}

 

private void sleep(long millis) {

try {

Thread.sleep(millis);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

 

/**

* 回收VelocityTracker对象。

*/

private void recycleVelocityTracker() {

mVelocityTracker.recycle();

mVelocityTracker = null;

}

 

float xDown1 = 0;

float yDown1 = 0;

boolean bScrollUpDown = false;

 

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

isScrolling = false;

xDown = 0;

yDown = 0;

xDown1 = ev.getX();

yDown1 = ev.getY();

bScrollUpDown = false;

break;

case MotionEvent.ACTION_MOVE:

float moveX = ev.getX();

float moveY = ev.getY();

float disX = moveX - xDown1;

float disY = moveY - yDown1;

if (Math.abs((int) disY) > Utils.dp2Px(mContext, moveMinValue)) {

bScrollUpDown = Math.abs(disX * 1000) < Math.abs(disY * 1000);

}

return bScrollUpDown;

case MotionEvent.ACTION_UP:

break;

}

boolean bRet = super.onInterceptTouchEvent(ev);

return bRet;

}

}

 

//3.自定义子布局控件

import android.content.Context;

import android.graphics.Rect;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

import android.widget.RelativeLayout;

import android.widget.ScrollView;

import com.bw.movie.utils.Utils;

 

public class InnerScrollView extends ScrollView {

/**

*/

private int moveMinValue = 20;

public RelativeLayout parentView;

 

public InnerScrollView(Context context, AttributeSet attrs) {

super(context, attrs);

 

}

 

private int lastScrollDelta = 0;

 

public void resume() {

overScrollBy(0, -lastScrollDelta, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);

lastScrollDelta = 0;

}

 

int mTop = 10;

 

/**

* 将targetView滚到最顶端

*/

public void scrollTo(View targetView) {

int oldScrollY = getScrollY();

int top = targetView.getTop() - mTop;

int delatY = top - oldScrollY;

lastScrollDelta = delatY;

overScrollBy(0, delatY, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);

}

 

private int getScrollRange() {

int scrollRange = 0;

if (getChildCount() > 0) {

View child = getChildAt(0);

scrollRange = Math.max(0, child.getHeight() - (getHeight()));

}

return scrollRange;

}

 

int currentY;

 

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

if (parentView == null) {

return super.onInterceptTouchEvent(ev);

} else {

 

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

// 将父scrollview的滚动事件拦截

currentY = (int) ev.getY();

setParentScrollAble(false);

// Debuglog.i("innerScroll", "ACTION_DOWN");

return super.onInterceptTouchEvent(ev);

} else if (ev.getAction() == MotionEvent.ACTION_UP) {

// 把滚动事件恢复给父Scrollview

setParentScrollAble(true);

// Debuglog.i("innerScroll", "ACTION_UP");

} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {

float moveY = ev.getY();

float dixY = moveY - currentY;

if (Math.abs(dixY) > Utils.dp2Px(getContext(), moveMinValue)) {

// Debuglog.i("innerScroll", "ACTION_MOVE");

return true;

}

//返回true把事件交给onTounch去处理

//return true;

}

}

return super.onInterceptTouchEvent(ev);

 

}

 

@Override

public boolean onTouchEvent(MotionEvent ev) {

View child = getChildAt(0);

try {

if (parentView != null) {

if (ev.getAction() == MotionEvent.ACTION_MOVE) {

int height = child.getMeasuredHeight();

height = height - getMeasuredHeight();

 

// System.out.println("height=" + height);

int scrollY = getScrollY();

// System.out.println("scrollY" + scrollY);

int y = (int) ev.getY();

 

// 手指向下滑动

if (currentY < y) {

if (scrollY <= 0) {

// 如果向下滑动到头,就把滚动交给父Scrollview

setParentScrollAble(true);

return false;

} else {

setParentScrollAble(false);

 

}

} else if (currentY > y) {

if (scrollY >= height) {

// 如果向上滑动到头,就把滚动交给父Scrollview

setParentScrollAble(true);

return false;

} else {

setParentScrollAble(false);

 

}

 

}

currentY = y;

}

}

return super.onTouchEvent(ev);

} catch (IllegalArgumentException e) {

// TODO: handle exception

}

return false;

}

 

/**

* 是否把滚动事件交给父scrollview

*

* @param flag

*/

private void setParentScrollAble(boolean flag) {

parentView.requestDisallowInterceptTouchEvent(!flag);

}

 

@Override

protected void onScrollChanged(int l, int t, int oldl, int oldt) {

if(listener!=null){

listener.onScrollChanged(l, t, oldl, oldt);

}

if(getScrollY() + getHeight() == computeVerticalScrollRange())

{

if (listener != null) {

int scrollY = getScrollY();

View childView=getChildAt(0);

if (scrollY==childView.getHeight()-getHeight()){

listener.scrollBottom();

}

}

}

}

 

ScrollViewListener listener;

public void setScrollViewListener(ScrollViewListener listener){

this.listener=listener;

}

public interface ScrollViewListener{

public void scrollBottom();

void onScrollChanged(int l, int t, int oldl, int oldt);

}

 

@Override

protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {

return 0;

}

}

//4.自定控件回调监听接口

public interface UpAndDownListener1 {

void onClose();

void onOpen();

void onAssignHeight(int height);

}

 

//使用方法

1、获取控件

upAndDownView = (UpAndDownView) findViewById(R.id.up_down_view);

upAndDownView.setMaxHeight(Constant.POI_DEFAULT_SHOW_HEIGHT_MID);

final TextView tv_title = upAndDownView.findViewById(R.id.bottom_tv_title);

upAndDownView.showView();

upAndDownView.hideView();

 

2、设置显示、隐藏自定义控件

public void showUpDownView() {

RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) upAndDownView

.getLayoutParams();

upAndDownView.setDefaultShowHeight(Constant.POI_DEFAULT_SHOW_HEIGHT_BOTTOM);

params.height = Utils.dp2Px(MovieInfoActivity.this, Constant.POI_DEFAULT_SHOW_HEIGHT_BOTTOM);

upAndDownView.setLayoutParams(params);

upAndDownView.setVisibility(View.VISIBLE);

}

 

public void hideUpDownView() {

upAndDownView.setVisibility(View.GONE);

}

//6布局

 

<com.bw.movie.ui.activity.UpAndDownView

android:id="@+id/up_down_view"

android:visibility="gone"

android:layout_alignParentBottom="true"

android:layout_width="match_parent"

android:layout_height="match_parent">

</com.bw.movie.ui.activity.UpAndDownView>

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

 

<RelativeLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:id="@+id/title">

 

<TextView

android:id="@+id/bottom_tv_title"

android:layout_width="90dp"

android:layout_height="35dp"

android:layout_centerHorizontal="true"

android:background="@drawable/tuoyuan_shape4"

android:gravity="center"

android:text="选择影院"

android:textColor="@color/white"

android:textSize="16sp" />

</RelativeLayout>

 

<com.bw.movie.view.InnerScrollView

android:id="@+id/scrollView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_below="@id/title"

android:background="@color/floralwhite"

android:fillViewport="true">

<LinearLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

<TextView

android:id="@+id/bottom_tv_date"

android:layout_width="match_parent"

android:layout_height="35dp"

android:background="@color/BulueTheme"

android:gravity="center"

android:text="今天(8月23日)"

android:textColor="@color/white" />

<android.support.v7.widget.RecyclerView

android:id="@+id/bottom_recycler"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

</LinearLayout>

</com.bw.movie.view.InnerScrollView>

</RelativeLayout>

//githup:https://github.com/caolinxing/UpDownDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值