国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android制作簡(jiǎn)單垂直上拉下滑View效果

2019-12-12 03:56:50
字體:
供稿:網(wǎng)友

一、簡(jiǎn)介

最近朋友公司需要實(shí)現(xiàn)一個(gè)垂直上拉下滑的View,該View最初只有一部分顯示在屏幕最下方,上拉那一部分可以將該View全部拉出來并全部顯示在屏幕上,下滑該View可以將該View隱藏在屏幕下。
先看一下最終實(shí)現(xiàn)效果吧。

二、實(shí)現(xiàn)思路

1、這個(gè)效果其實(shí)有很多實(shí)現(xiàn)方法,為了讓松手時(shí)有一個(gè)viewpager一樣的緩慢滑動(dòng)的效果我選擇用scrollBy配合Scroller,應(yīng)該是既方便又實(shí)用的。
2、這個(gè)View的設(shè)計(jì)是這樣的:
(1)將這個(gè)View的子view通過layout放在該View下面;
(2)通過重寫onTouchEvent方法給這個(gè)子View滑動(dòng)效果,在MOVE_UP的動(dòng)作給這個(gè)子View加上Scroller平滑到View的頂部或者底部。
見圖:

三、實(shí)現(xiàn)

1、先自定義一個(gè)屬性,表示子View應(yīng)該有多少部分露在外面,也就是上圖中紅色和綠色相交的部分。
在res文件夾-values文件夾下面創(chuàng)建一個(gè)attrs.xml文件

attrs.xml :

<resources> <declare-styleable name="MyScrollerView">  <attr name="visibility_height" format="dimension"></attr> </declare-styleable></resources>

在XML文件中引用該屬性:

<com.zw.myfirstapp.MyScrollerView   android:layout_width="match_parent"   android:layout_height="wrap_content"   android:layout_alignParentBottom="true"   android:background="@android:color/transparent"   android:id="@+id/msv"   app:visibility_height="100dp"   ></com.zw.myfirstapp.MyScrollerView>

在代碼中調(diào)用該屬性(該View名字為MyScrollerView,我圖方便繼承的是LinearLayout,繼承ViewGroup或者其他的布局View都可以):

public MyScrollerView(Context context) {  this(context,null); } public MyScrollerView(Context context, AttributeSet attrs) {  this(context, attrs,0); } public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyScrollerView);  visibilityHeight = ta.getDimension(R.styleable.MyScrollerView_visibility_height,200);  ta.recycle();  init(context); }

2、重寫onFinishInflate方法,重寫該方法的原因是我希望我只有一個(gè)子View,這樣就好確定滑動(dòng)的高度,不然我還需要重新計(jì)算子View們的高度總和,比較麻煩。這個(gè)方法會(huì)在onMeasure之前調(diào)用。

 @Override protected void onFinishInflate() {  super.onFinishInflate();  if(getChildCount() == 0 || getChildAt(0) == null){   throw new RuntimeException("沒有子控件!");  }  if(getChildCount() > 1){   throw new RuntimeException("只能有一個(gè)子控件!");  }  mChild = getChildAt(0); }

3、init方法里做一些初始化操作,比如說創(chuàng)建一個(gè)Scroller對(duì)象,給View的背景設(shè)為透明:

 private void init(Context context) {  mScroller = new Scroller(context);  this.setBackgroundColor(Color.TRANSPARENT); }

4、重寫onMeasure方法和onLayout方法,確定可以滑動(dòng)的最大高度,以及子View的排列位置(其實(shí)可以不用重寫onMeasure,我這樣寫只是習(xí)慣)。

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  super.onMeasure(widthMeasureSpec, heightMeasureSpec);  mScrollHeight = (int) (mChild.getMeasuredHeight() - visibilityHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {  super.onLayout(changed, l, t, r, b);  mChild.layout(0,mScrollHeight,mChild.getMeasuredWidth(),mChild.getMeasuredHeight() + mScrollHeight); }

5、先看我定義的成員變量的含義吧:

 /**  * downY:手指按下時(shí)距離View頂部的距離  * moveY:手指在屏幕上滑動(dòng)的距離(不停變化)  * movedY:手指在屏幕上總共滑動(dòng)的距離(為了確定手指一共滑動(dòng)了多少距離,不能超過可滑動(dòng)的最大距離)  */ private int downY,moveY,movedY; //子View private View mChild; private Scroller mScroller; //可滑動(dòng)的最大距離 private int mScrollHeight; //子View是否在頂部 private boolean isTop = false; //最初子View在View內(nèi)可見的高度 private float visibilityHeight;

6、重寫onTouchEvent方法,做滑動(dòng)判斷,解釋都寫在注釋里了:

 @Override public boolean onTouchEvent(MotionEvent event) {  switch (event.getAction()){   case MotionEvent.ACTION_DOWN:    //手指按下時(shí)距離View上面的距離    downY = (int) event.getY();    //如果子View不在頂部 && 按下的位置在子View沒有顯示的位置,則不消費(fèi)此次滑動(dòng)事件,否則消費(fèi)    if(!isTop && downY < mScrollHeight ){     return super.onTouchEvent(event);    }    return true;   case MotionEvent.ACTION_MOVE:    moveY = (int) event.getY();    //deY是滑動(dòng)的距離,向上滑時(shí)deY>0 ,向下滑時(shí)deY<0    int deY = downY - moveY;    //向上滑動(dòng)時(shí)的處理    if(deY > 0){     //將每次滑動(dòng)的距離相加,為了防止子View的滑動(dòng)超過View的頂部     movedY += deY;     if(movedY > mScrollHeight) movedY = mScrollHeight;     if(movedY < mScrollHeight){      scrollBy(0,deY);      downY = moveY;      return true;     }    }    //向下滑動(dòng)時(shí)的處理,向下滑動(dòng)時(shí)需要判斷子View是否在頂部,如果不在頂部則不消費(fèi)此次事件    if(deY < 0 && isTop){     movedY += deY;     if(movedY < 0 ) movedY = 0;     if(movedY > 0){      scrollBy(0,deY);     }     downY = moveY;     return true;    }    break;   case MotionEvent.ACTION_UP:    //手指抬起時(shí)的處理,如果向上滑動(dòng)的距離超過了最大可滑動(dòng)距離的1/4,并且子View不在頂部,就表示想把它拉上去    if(movedY > mScrollHeight / 4 && !isTop){     mScroller.startScroll(0,getScrollY(),0,(mScrollHeight - getScrollY()));     invalidate();     movedY = mScrollHeight;     isTop = true;    }else {     //否則就表示放棄本次滑動(dòng),讓它滑到最初的位置     mScroller.startScroll(0,getScrollY(),0, -getScrollY());     postInvalidate();     movedY = 0;     isTop = false;    }    break;  }  return super.onTouchEvent(event); }

7、最后要重寫一個(gè)computeScroll方法,該方法是用來配合scroller的:

 @Override public void computeScroll() {  super.computeScroll();  if(mScroller.computeScrollOffset()){   scrollTo(0,mScroller.getCurrY());   postInvalidate();  } }

8、關(guān)于scroller的用法,可參考郭霖的這篇博客:http://blog.csdn.net/guolin_blog/article/details/48719871

四、完整代碼:

xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent">  <com.zw.myfirstapp.MyScrollerView   android:layout_width="match_parent"   android:layout_height="wrap_content"   android:layout_alignParentBottom="true"   android:background="@android:color/transparent"   android:id="@+id/msv"   app:visibility_height="100dp"   >   <LinearLayout    android:layout_width="match_parent"    android:layout_height="300dp"    android:background="@mipmap/b"    android:gravity="center"    android:orientation="vertical">    <Button     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:id="@+id/btn"     android:text="我是一個(gè)按鈕"/>   </LinearLayout>  </com.zw.myfirstapp.MyScrollerView></RelativeLayout>

MyScrollerView:

public class MyScrollerView extends LinearLayout { /**  * downY:手指按下時(shí)距離View頂部的距離  * moveY:手指在屏幕上滑動(dòng)的距離(不停變化)  * movedY:手指在屏幕上總共滑動(dòng)的距離(為了確定手指一共滑動(dòng)了多少距離,不能超過可滑動(dòng)的最大距離)  */ private int downY,moveY,movedY; //子View private View mChild; private Scroller mScroller; //可滑動(dòng)的最大距離 private int mScrollHeight; //子View是否在頂部 private boolean isTop = false; //最初子View在View內(nèi)可見的高度 private float visibilityHeight; public MyScrollerView(Context context) {  this(context,null); } public MyScrollerView(Context context, AttributeSet attrs) {  this(context, attrs,0); } public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyScrollerView);  visibilityHeight = ta.getDimension(R.styleable.MyScrollerView_visibility_height,200);  ta.recycle();  init(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  super.onMeasure(widthMeasureSpec, heightMeasureSpec);  mScrollHeight = (int) (mChild.getMeasuredHeight() - visibilityHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {  super.onLayout(changed, l, t, r, b);  mChild.layout(0,mScrollHeight,mChild.getMeasuredWidth(),mChild.getMeasuredHeight() + mScrollHeight); } private void init(Context context) {  mScroller = new Scroller(context);  this.setBackgroundColor(Color.TRANSPARENT); } @Override protected void onFinishInflate() {  super.onFinishInflate();  if(getChildCount() == 0 || getChildAt(0) == null){   throw new RuntimeException("沒有子控件!");  }  if(getChildCount() > 1){   throw new RuntimeException("只能有一個(gè)子控件!");  }  mChild = getChildAt(0); } @Override public boolean onTouchEvent(MotionEvent event) {  switch (event.getAction()){   case MotionEvent.ACTION_DOWN:    //手指按下時(shí)距離View上面的距離    downY = (int) event.getY();    //如果子View不在頂部 && 按下的位置在子View沒有顯示的位置,則不消費(fèi)此次滑動(dòng)事件,否則消費(fèi)    if(!isTop && downY < mScrollHeight ){     return super.onTouchEvent(event);    }    return true;   case MotionEvent.ACTION_MOVE:    moveY = (int) event.getY();    //deY是滑動(dòng)的距離,向上滑時(shí)deY>0 ,向下滑時(shí)deY<0    int deY = downY - moveY;    //向上滑動(dòng)時(shí)的處理    if(deY > 0){     //將每次滑動(dòng)的距離相加,為了防止子View的滑動(dòng)超過View的頂部     movedY += deY;     if(movedY > mScrollHeight) movedY = mScrollHeight;     if(movedY < mScrollHeight){      scrollBy(0,deY);      downY = moveY;      return true;     }    }    //向下滑動(dòng)時(shí)的處理,向下滑動(dòng)時(shí)需要判斷子View是否在頂部,如果不在頂部則不消費(fèi)此次事件    if(deY < 0 && isTop){     movedY += deY;     if(movedY < 0 ) movedY = 0;     if(movedY > 0){      scrollBy(0,deY);     }     downY = moveY;     return true;    }    break;   case MotionEvent.ACTION_UP:    //手指抬起時(shí)的處理,如果向上滑動(dòng)的距離超過了最大可滑動(dòng)距離的1/4,并且子View不在頂部,就表示想把它拉上去    if(movedY > mScrollHeight / 4 && !isTop){     mScroller.startScroll(0,getScrollY(),0,(mScrollHeight - getScrollY()));     invalidate();     movedY = mScrollHeight;     isTop = true;    }else {     //否則就表示放棄本次滑動(dòng),讓它滑到最初的位置     mScroller.startScroll(0,getScrollY(),0, -getScrollY());     postInvalidate();     movedY = 0;     isTop = false;    }    break;  }  return super.onTouchEvent(event); } @Override public void computeScroll() {  super.computeScroll();  if(mScroller.computeScrollOffset()){   scrollTo(0,mScroller.getCurrY());   postInvalidate();  } }}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 子洲县| 苏尼特左旗| 临潭县| 道真| 巫山县| 奉新县| 安阳市| 海伦市| 扬州市| 昌宁县| 朔州市| 固镇县| 鄂尔多斯市| 定州市| 锡林浩特市| 明光市| 清河县| 商丘市| 汾西县| 兰州市| 枝江市| 彩票| 寿阳县| 肇东市| 江山市| 无锡市| 德清县| 长岭县| 柳林县| 和田县| 阜康市| 阳新县| 凯里市| 曲水县| 桂林市| 泰宁县| 汝城县| 阿坝县| 轮台县| 阿坝县| 郁南县|