1. 程式人生 > >自定義拉取選單

自定義拉取選單

//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>