Android 下拉式抽屉折叠动画
自定義listview工具類1、
public class ViewMeasureUtils {/*** 根據父 View 規則和子 View 的 LayoutParams,計算子類的寬度(width)測量規則** @param view*/public static int getChildWidthMeasureSpec(View view, int parentWidthMeasureSpec) {// 獲取父 View 的測量模式int parentWidthMode = MeasureSpec.getMode(parentWidthMeasureSpec);// 獲取父 View 的測量尺寸int parentWidthSize = MeasureSpec.getSize(parentWidthMeasureSpec);// 定義子 View 的測量規則int childWidthMeasureSpec = 0;// 獲取子 View 的 LayoutParamsViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) view.getLayoutParams();if (parentWidthMode == MeasureSpec.EXACTLY || parentWidthMode == MeasureSpec.AT_MOST) {/* 這是當父類的模式是 dp 的情況 */if (layoutParams.width > 0) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);} else if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidthSize, MeasureSpec.AT_MOST);} else if (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidthSize, MeasureSpec.EXACTLY);}} else if (parentWidthMode == MeasureSpec.UNSPECIFIED) {/* 這是當父類的模式是 MATCH_PARENT 的情況 */if (layoutParams.width > 0) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);} else if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);} else if (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);}}// 返回子 View 的測量規則return childWidthMeasureSpec;}/*** 根據父 View 規則和子 View 的 LayoutParams,計算子類的寬度(width)測量規則** @param view*/public static int getChildHeightMeasureSpec(View view, int parentHeightMeasureSpec) {// 獲取父 View 的測量模式int parentHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);// 獲取父 View 的測量尺寸int parentHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec);// 定義子 View 的測量規則int childHeightMeasureSpec = 0;// 獲取子 View 的 LayoutParamsViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) view.getLayoutParams();if (parentHeightMode == MeasureSpec.EXACTLY || parentHeightMode == MeasureSpec.AT_MOST) {/* 這是當父類的模式是 dp 的情況 */if (layoutParams.height > 0) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);} else if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeightSize, MeasureSpec.AT_MOST);} else if (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeightSize, MeasureSpec.EXACTLY);}} else if (parentHeightMode == MeasureSpec.UNSPECIFIED) {/* 這是當父類的模式是 MATCH_PARENT 的情況 */if (layoutParams.height > 0) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);} else if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);} else if (layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);}}// 返回子 View 的測量規則return childHeightMeasureSpec;} }?
自定義listview、
public class SlidingMenuVertical extends LinearLayout {
? ? private Scroller mScroller;
? ? private View view_top;
? ? private View view_bottom;
? ? private float downX;
? ? private float downY;
? ? private boolean opened = true;//狀態是否開閉
? ? private OnSwitchListener onSwitchListener;
? ? private int duration_max = 300;//最長過度時間
? ? private int ambit_scroll = 100;//滑動界限,開閉
? ? private int y_opened = -1; ? ?// * y_opened:抽屜打開時view_bootom的top y
? ? public SlidingMenuVertical(Context context) {
? ? ? ? this(context, null);
? ? }
? ? public SlidingMenuVertical(Context context, AttributeSet attrs) {
? ? ? ? super(context, attrs);
? ? ? ? mScroller = new Scroller(context);
? ? ? ? setOrientation(VERTICAL);
? ? }
? ? @Override
? ? protected void onFinishInflate() {
? ? ? ? // 當xml解析完成時的回調
? ? ? ? view_top = getChildAt(0);
? ? ? ? view_bottom = getChildAt(1);
? ? }
? ? @Override
? ? protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
? ? ? ? super.onMeasure(widthMeasureSpec, heightMeasureSpec);
? ? ? ? view_top.measure(widthMeasureSpec, ViewMeasureUtils.getChildHeightMeasureSpec(view_top, heightMeasureSpec));
// ? ? ? ?view_middle.measure(widthMeasureSpec,ViewMeasureUtils.getChildHeightMeasureSpec(view_middle,heightMeasureSpec));
? ? ? ? view_bottom.measure(widthMeasureSpec, heightMeasureSpec);
? ? }
? ? @Override
? ? public boolean onInterceptTouchEvent(MotionEvent event) {
? ? ? ? setY_opened();
? ? ? ? // 攔截
? ? ? ? // 豎直滑動時,去攔截
? ? ? ? switch (event.getAction()) {
? ? ? ? ? ? case MotionEvent.ACTION_DOWN:
? ? ? ? ? ? ? ? downX = event.getX();
? ? ? ? ? ? ? ? downY = event.getY();
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_MOVE:
? ? ? ? ? ? ? ? float moveX = event.getX();
? ? ? ? ? ? ? ? float moveY = event.getY();
? ? ? ? ? ? ? ? // 豎直滑動
? ? ? ? ? ? ? ? if (Math.abs(moveY - downY) > Math.abs(moveX - downX)) {
? ? ? ? ? ? ? ? ? ? //上面隱藏
? ? ? ? ? ? ? ? ? ? if (opened == false) {
? ? ? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? //上面顯示并且下滑
? ? ? ? ? ? ? ? ? ? if (opened == true && (moveY - downY) > 0) {
? ? ? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_UP:
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? return super.onInterceptTouchEvent(event);
? ? }
? ? @Override
? ? public boolean onTouchEvent(MotionEvent event) {
? ? ? ? switch (event.getAction()) {
? ? ? ? ? ? case MotionEvent.ACTION_DOWN:
? ? ? ? ? ? ? ? downX = event.getX();
? ? ? ? ? ? ? ? downY = event.getY();
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_MOVE:
? ? ? ? ? ? ? ? float moveX = event.getX();
? ? ? ? ? ? ? ? float moveY = event.getY();
? ? ? ? ? ? ? ? int dy = (int) (downY - moveY + 0.5f);// 四舍五入 20.9 + 0.5-->20
// ? ? ? ? ? ? ? ?Log.e("dy","++++++++++++++++++++++++++++"+dy);
? ? ? ? ? ? ? ? int scrollY = getScrollY();
? ? ? ? ? ? ? ? //mDownY - moveY>0上滑
? ? ? ? ? ? ? ? if (scrollY + dy > 0) {
? ? ? ? ? ? ? ? ? ? scrollBy(0, dy);
? ? ? ? ? ? ? ? ? ? if (scrollY + dy > getHeight_top()) {
? ? ? ? ? ? ? ? ? ? ? ? scrollTo(0, getHeight_top());
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? downX = moveX;
? ? ? ? ? ? ? ? downY = moveY;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_UP:
// ? ? ? ? ? ? ? ?Log.e("heigth_top", "+++++++++++++++++" + height_top);
// ? ? ? ? ? ? ? ?Log.e("scrollY", "+++++++++++++++++" + getScrollY());
? ? ? ? ? ? ? ? if (opened) {
? ? ? ? ? ? ? ? ? ? open(!(getScrollY() > ambit_scroll || getScrollY() > getHeight_top() / 3));
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? open(getScrollY() < getHeight_top() - ambit_scroll || getScrollY() < getHeight_top() * 2 / 3);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? // 消費掉
? ? ? ? return true;
? ? }
? ? /**
? ? ?* 開閉抽屜
? ? ?*
? ? ?* @param open
? ? ?*/
? ? public void open(boolean open) {
? ? ? ? setY_opened();
? ? ? ? this.opened = open;
? ? ? ? //打開
? ? ? ? if (open) {
// ? ? ? ? ? ?Log.e("打開", "+++++++++++++++++++++++++++++");
? ? ? ? ? ? int startX = getScrollX();// 起始的坐標X
? ? ? ? ? ? int startY = getScrollY();// 起始的坐標Y
? ? ? ? ? ? int endX = 0;
? ? ? ? ? ? int endY = 0;
? ? ? ? ? ? int dx = endX - startX;// 增量X
? ? ? ? ? ? int dy = endY - startY;// 增量Y
? ? ? ? ? ? // 1px = 10
? ? ? ? ? ? int duration = Math.abs(dy) * 10;
? ? ? ? ? ? if (duration > duration_max) {
? ? ? ? ? ? ? ? duration = duration_max;
? ? ? ? ? ? }
? ? ? ? ? ? mScroller.startScroll(startX, startY, dx, dy, duration);
? ? ? ? } else {
? ? ? ? ? ? Log.e("關閉", "+++++++++++++++++++++++++++++" + getScrollY());
? ? ? ? ? ? int startX = getScrollX();// 起始的坐標X
? ? ? ? ? ? int startY = getScrollY();// 起始的坐標Y
? ? ? ? ? ? int endX = 0;
? ? ? ? ? ? int endY = getHeight_top();
? ? ? ? ? ? int dx = endX - startX;// 增量X
? ? ? ? ? ? int dy = endY - startY;// 增量Y
? ? ? ? ? ? // 1px = 10
? ? ? ? ? ? int duration = Math.abs(dy) * 10;
? ? ? ? ? ? if (duration > duration_max) {
? ? ? ? ? ? ? ? duration = duration_max;
? ? ? ? ? ? }
? ? ? ? ? ? // 模擬數據變化
? ? ? ? ? ? mScroller.startScroll(startX, startY, dx, dy, duration);
? ? ? ? }
? ? ? ? invalidate();// 觸發ui繪制 --> draw() --> dispatchDraw()--> drawChild -->
? ? }
? ? @Override
? ? public void computeScroll() {
? ? ? ? if (mScroller.computeScrollOffset()) {// 如果正在計算的過程中
? ? ? ? ? ? // 更新滾動的位置
? ? ? ? ? ? scrollTo(0, mScroller.getCurrY());
? ? ? ? ? ? invalidate();
? ? ? ? }
? ? }
? ? @Override
? ? protected void onScrollChanged(int l, int t, int oldl, int oldt) {
? ? ? ? super.onScrollChanged(l, t, oldl, oldt);
// ? ? ? ?Log.e("y_now", ScreenUtils.getViewScreenLocation(view_bottom)[1] + "++++++++++++++++++++++");
//
// ? ? ? ?Log.e("y_closed", y_opened - height_top + "++++++++++++++++++++++");
? ? ? ? if (onSwitchListener != null) {
? ? ? ? ? ? onSwitchListener.onSwitching(t - oldt < 0 ? true : false,
? ? ? ? ? ? ? ? ? ? getY_now(), getY_opened(), getY_opened() - getHeight_top());
? ? ? ? ? ? if (getY_now() == getY_opened()) {
// ? ? ? ? ? ? ? ?Log.e("true", "++++++++++++++++++++++++");
? ? ? ? ? ? ? ? onSwitchListener.onSwitched(true);
? ? ? ? ? ? }
? ? ? ? ? ? if (getY_now() == getY_opened() - getHeight_top()) {
// ? ? ? ? ? ? ? ?Log.e("false", "++++++++++++++++++++++++");
? ? ? ? ? ? ? ? onSwitchListener.onSwitched(false);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? public boolean isOpened() {
? ? ? ? return opened;
? ? }
? ? public int getDuration_max() {
? ? ? ? return duration_max;
? ? }
? ? /**
? ? ?* 設置松手后 開閉最長過渡時間
? ? ?*
? ? ?* @param duration_max
? ? ?*/
? ? public void setDuration_max(int duration_max) {
? ? ? ? this.duration_max = duration_max;
? ? }
? ? public View getView_top() {
? ? ? ? return view_top;
? ? }
? ? public View getView_bottom() {
? ? ? ? return view_bottom;
? ? }
? ? public int getHeight_top() {
? ? ? ? return view_top.getMeasuredHeight();
? ? }
? ? /**
? ? ?* 獲取 * y_opened:抽屜打開時view_bootom的top y
? ? ?*/
? ? private void setY_opened(){
? ? ? ? if (y_opened<0){
? ? ? ? ? ? y_opened=getViewScreenLocation(view_bottom)[1];
? ? ? ? ? ? Log.e("y _open",y_opened+"++++++++++++++++++++");
? ? ? ? }
? ? }
? ? /**
? ? ?* y_opened:抽屜打開時view_bootom的top y
? ? ?*
? ? ?* @return
? ? ?*/
? ? public int getY_opened() {
? ? ? ? if (y_opened<0){
? ? ? ? ? ? Log.e("還未計算出來","+++++++++++++++++++++++++++++++++++");
? ? ? ? ? ? return 0;
? ? ? ? }
? ? ? ? return y_opened;
? ? }
? ? /**
? ? ?* y_now:抽屜實時view_bootom的top y
? ? ?*
? ? ?* @return
? ? ?*/
? ? public int getY_now() {
? ? ? ? return getViewScreenLocation(view_bottom)[1];
? ? }
? ? public int getAmbit_scroll() {
? ? ? ? return ambit_scroll;
? ? }
? ? /**
? ? ?* 修改滑動界限 值,值越大 ?開閉越難 ?單位ms
? ? ?*
? ? ?* @param ambit_scroll <height_top
? ? ?*/
? ? public void setAmbit_scroll(int ambit_scroll) {
? ? ? ? this.ambit_scroll = ambit_scroll;
? ? }
? ? /**
? ? ?* 計算指定的 View 在屏幕中的坐標。
? ? ?*/
? ? public ?int[] getViewScreenLocation(View view) {
? ? ? ? int[] location = new int[2];
? ? ? ? // 獲取控件在屏幕中的位置,返回的數組分別為控件左頂點的 x、y 的值
? ? ? ? view.getLocationOnScreen(location);
? ? ? ? return location;
? ? }
? ? public interface OnSwitchListener {
? ? ? ? /*
? ? ? ? 滑動中
? ? ? ? y_now:實時view_bottom的top y, y_opened:抽屜打開時view_bootom的top y,y_closed:抽屜關閉時view_bottom的top y ?top y:在屏幕中的top y坐標
? ? ? ? ?*/
? ? ? ? public void onSwitching(boolean isToOpen, int y_now, int y_opened, int y_closed);
? ? ? ? /*
? ? ? ? 滑動停止,狀態是否開閉
? ? ? ? ?*/
? ? ? ? public void onSwitched(boolean opened);
? ? }
? ? public void setOnSwitchListener(OnSwitchListener onSwitchListener) {
? ? ? ? this.onSwitchListener = onSwitchListener;
? ? }
}
2、activity實現代碼,可打開可關閉抽屜動畫,可監聽動畫距離——漸變效果
final TextView tv_middle = (TextView) findViewById(R.id.tv_middle); final SlidingMenuVertical slidingMenuVertical = ((SlidingMenuVertical) findViewById(R.id.slidingMenu)); slidingMenuVertical.setDuration_max(2300); slidingMenuVertical.setAmbit_scroll(100); slidingMenuVertical.setOnSwitchListener(new SlidingMenuVertical.OnSwitchListener() {/*滑動中 y_now:實時view_bottom的top y, y_opened:抽屜打開時view_bootom的top y,y_closed:抽屜關閉時view_bottom的top y top y:在屏幕中的top y坐標*/@Overridepublic void onSwitching(boolean isToOpen, int y_now, int y_opened, int y_closed) {tv_middle.setBackgroundColor(Color.argb((int) (1.0f * (y_opened - y_now) / (y_opened - y_closed) * 255),Color.red(0xff3F51B5), Color.green(0xff3F51B5), Color.blue(0xff3F51B5)));tv_middle.setTextColor(Color.argb((int) (1.0f * (y_opened - y_now) / (y_opened - y_closed) * 255),Color.red(0xffffffff), Color.green(0xffffffff), Color.blue(0xffffffff)));}@Overridepublic void onSwitched(boolean opened) {if (opened) {tv_middle.setBackgroundColor(0xffffffff);tv_middle.setTextColor(0xff454545);}} });findViewById(R.id.tv_switch).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {slidingMenuVertical.open(!slidingMenuVertical.isOpened());} });?
3、layout.xml文件。
說明:SlidingMenuVertical里面第一個item就是抽屜內容——可以是view,可以是ViewGroup,第二個item就是抽屜下面的內容,第二個item里面ScrollView外部view可開關抽屜內容
<com.tianxin.choutis.SlidingMenuVerticalandroid:id="@+id/myct"android:layout_below="@+id/kgte"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/myte"android:layout_width="match_parent"android:layout_height="150dp"android:background="@color/colorAccent"android:text="Hello World!" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv_middle"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textColor="#454545" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="#e6e6e6"android:overScrollMode="never"android:scrollbars="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="下面\n下面\n下面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n" /></ScrollView></LinearLayout></com.tianxin.choutis.SlidingMenuVertical>總結
以上是生活随笔為你收集整理的Android 下拉式抽屉折叠动画的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 兴业数金牛客java笔试题
- 下一篇: 安装C语言版本tensorflow