相关文章:
1、《PullScrollView详解(一)——自定义控件属性》
2、《PullScrollView详解(二)——Animation、Layout与下拉回弹》
3、《PullScrollView详解(三)——PullScrollView实现》
4、《PullScrollView详解(四)——完全使用listview实现下拉回弹(方法一)》
5、《PullScrollView详解(五)——完全使用listview实现下拉回弹(方法二)》
6、《PullScrollView详解(六)——延伸拓展(listview中getScrollY()一直等于0、ScrollView中的overScrollBy)》
言归正转,这篇就是终极篇了,一般来讲,终极篇总是最有难度的,已经折磨大家四篇文章,这篇文章也终于千呼万唤始出来了。在listview中实现下拉回弹是比较有难度的,因为在listview中有关scroll各方面的获取都需要自己来做,所以整体难度还是比较大。废话不多说,现在开整吧。
先来看看效果图:(与PullScrollView的效果是一样一样的)
一、搭框架
同前几篇一样,先出框架。这篇将是前几篇的集大成者,一些细小的部分,就不再细讲了,如果有疑问,我会在后面列出每个忽略未细讲的知识点所在的博客位置,大家可以先移步过去看看。在这部分,我们要完成下面这个效果:
1、创建PullScrollListView 派生自ListView
- public class PullScrollListView extends ListView {
- //用户定义的手指可移动的最大高度,在这里,手指移动距离是content移动距离的两倍,是header移动距离的四倍
- private int mContentMaxMoveHeight = 0;
- public PullScrollListView(Context context) {
- super(context);
- }
- public PullScrollListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
- public PullScrollListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context, attrs);
- }
- private void init(Context context, AttributeSet attrs) {
- if (null != attrs) {
- TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PullScrollView);
- if (ta != null) {
- mContentMaxMoveHeight = (int) ta.getDimension(R.styleable.PullScrollView_maxMoveHeight, -1);
- ta.recycle();
- }
- }
- }
- }
2、main.xml 主布局
这个布局每篇文章都会讲一遍,没什么难度
- <?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <ImageView
- android:id="@+id/background_img"
- android:layout_width="match_parent"
- android:layout_height="400dp"
- android:layout_marginTop="-100dp"
- android:scaleType="fitXY"
- android:src="@drawable/pic3"/>
- <com.harvic.PullScrollListViewDemo.PullScrollListView
- android:id="@+id/pull_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:divider="@null"
- android:dividerPadding="0dp"
- android:dividerHeight="0dp"
- app:maxMoveHeight="200dp"
- app:headerTopHeight = "100dp"
- app:headerVisibleHeight="150dp"/>
- </FrameLayout>
同样,注意ImageView要定义android:layout_marginTop="-100dp",即初始向上移100px,以便向下移动。
3、填充布局
(1)、Item布局(item_layout.xml)
- <TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:minHeight="40dp"
- android:background="#ffffff"/>
有关构造数据,填充数据的部分也不再细讲了,每篇文章都会讲,这是listview构造的基础知识,大家应该能看明白。
- public class MainActivity extends Activity {
- private String[] mStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
- "Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
- "Allgauer Emmentaler"};
- private LinkedList<String> mListItems;
- private PullScrollListView mListView;
- private ArrayAdapter<String> mAdapter;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mListView = (PullScrollListView) findViewById(R.id.pull_list_view);
- mListItems = new LinkedList<String>();
- mListItems.addAll(Arrays.asList(mStrings));
- mAdapter = new ArrayAdapter<String>(this,R.layout.item_layout, mListItems);
- mListView.setAdapter(mAdapter);
- }
- }
4、添加Header
(1)、headerview的布局(headerview.xml)
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:clickable="false">
- <!--如果要让headview不可点击,在这里设置clickable="false"是没用的,只有通过ListView.addHeaderView(view,null,false);来设置-->
- </LinearLayout>
一定要注意的是,要通过listview的addHeaderView(View v, Object data, boolean isSelectable)函数来添加不可点击的headview,如果直接使用addHeaderView(View v)来添加,默认是可以点击的,当用户点击时,会变白。代码如下:
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mListView = (PullScrollListView) findViewById(R.id.pull_list_view);
- mListItems = new LinkedList<String>();
- mListItems.addAll(Arrays.asList(mStrings));
- mAdapter = new ArrayAdapter<String>(this,R.layout.item_layout, mListItems);
- LayoutInflater inflater = getLayoutInflater();
- View view = inflater.inflate(R.layout.headerview,mListView,false);
- //设置headerview不可点击
- mListView.addHeaderView(view,null,false);
- mListView.setAdapter(mAdapter);
- }
二、下拉回弹
1、添加topView
与前几篇一样,我们首先要讲小狗的图片对应的ImageView传给PullScrollListView,在它下拉的时候跟着一起下拉,在松开的时候,一起回弹。PullScrollListView.java中:
- //底部图片View
- private View mTopView;
- public void setmTopView(View view) {
- mTopView = view;
- }
在MainActivity.java中:将图片ImageView设置进去
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mListView = (PullScrollListView) findViewById(R.id.pull_list_view);
- ………………
- ImageView headerView = (ImageView)findViewById(R.id.background_img);
- mListView.setmTopView(headerView);
- }
2、点击时保存初始化位置
我们这里尽量保持 《PullScrollView详解(三)——PullScrollView实现》 的逻辑,在用户点击时做变量的初始化操作,初始化操作包括topView位置的初始化:用于在反弹时,回到初始化位置
contentView位置的初始化:同样用于在反弹时,回到初始化位置,但这里的contentVIew是listview本身,所以就是this变量。下面还会再讲。
点击位置的初始化:点击位置的初始化主要是用来计算当前用户移动了多少的。
这里还有其它变量的初始化,先列出完整代码,下面再细讲。
- //底部View
- private View mContentView = this;
- //初始点击位置
- private Point mTouchPoint = new Point();
- //头部图片的初始化位置
- private Rect mHeadInitRect = new Rect();
- //ScrollView的contentView的初始化位置
- private Rect mContentInitRect = new Rect();
- //标识当前view是否移动
- boolean mIsMoving = false;
- //是否关闭ListView的滑动.
- private boolean mEnableTouch = false;
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- //保存原始位置
- mTouchPoint.set((int) event.getX(), (int) event.getY());
- mHeadInitRect.set(mTopView.getLeft(), mTopView.getTop(), mTopView.getRight(), mTopView.getBottom());
- mContentInitRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
- mIsMoving = false;
- mEnableTouch = false;
- }
- return super.onInterceptTouchEvent(event);
- }
这里就不再进行拦截,因为没有拦截的意义。在PullScrollView中对ScrollView的ACTION_MOVE事件进行了拦截,是因为ScrollView本身还是有子控件的。有可能在OnTouchEvent中把ACTION_MOVE事件消费掉而传不到ScrollView里。
(1)、变量定义
在定义的变量里,其实没什么难度,与博客三不相同的一点是:
- private View mContentView = this;
(2)、初始化
上面也都说了,各个初始化的意义
- mTouchPoint.set((int) event.getX(), (int) event.getY());
- mHeadInitRect.set(mTopView.getLeft(), mTopView.getTop(), mTopView.getRight(), mTopView.getBottom());
- mContentInitRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
另外还有两个变量:
- mIsMoving = false;
- mEnableTouch = false;
mEnableTouch这个变量的解释就有点难度了,它是用来标识是否禁止控件自身的移动的。这里估计也说不清楚,大家先知道就好,下面用到时会再讲。
3、获取listview滚动高度
在讲解下拉之前,我们得先考虑一个问题,即如何判断什么时候该下拉了,在PullScrollView中我们可以通过getScrollY()==0来判断当前View是不是在顶部,如果在顶部而且又是在下拉,那么就可以下拉滚动了。
那在ListView中要怎么判断当前是不是到顶部了呢?大家可以尝试在listview中,getScrollY()始终等于0. 有关《延伸:为什么PullScrollView中getScrollY()有值而ListView中的getScrollY()却一直为零》的原因,可以参考在下一篇的延伸部分。
我这里就直接讲在listview中要怎么获取滚动高度的方法。先看看下面的这幅图:
在这幅图中,listview的item分别是两个高度不等的headview和各个item。
在图片下面被黄色框框起来的部分,代表屏幕。那黄色框以上的部分,就表示已经滚出去的部分,那么它的高度就是我们要计算的scrollHeight;
所以就这个图片当前状态而言
scrollHeight = 所有的header高度 + 已经滚动过去的所有Item的高度 + 当前可见item的滚动部分。
在正式开始之前先给大家讲ListView的两个函数:
- //获取当前可见item的索引
- public int getFirstVisiblePosition()
- //获取指定位置的item的视图
- public View getChildAt(int index)
getFirstVisiblePosition()
这个函数用于获取当前可见item的索引,索引是从第一个headerview开始算的,第一个headerview索引是0,向下逐个加1.需要注意的是,只要当前item还能看见一丢丢,就算可见哦。比如,我上面的图中,利用getFirstVisiblePosition()得到的值会是3.即第四个item,虽然它向上滚动了一半,但它仍是可见的,所以getFirstVisiblePosition()获取到的是第四个item.
getChildAt(int index)
这个函数需要非常注意,它的index索引是从当前可见的位置开始的。当前第一个可见的Item的索引是0!!!!!不是从第一个headerview开始的!!!!就上面那个图而言,由于当前可见的item是第四个item.这时候调用getChildAt(0)获得是第四个item的View!!!!一定要非常注意。
但正是这个特性正好,我们可以使用getChildAt(0)来获得当前第一个可见item的视图,来得到它滚动的距离。
好了,介绍完计算方法和这两个函数,下面我们就开始看通过代码怎么计算滚动高度吧。
(1)、重写addHeaderView(View v)
因为我们会计算headview的高度,那我们需要得到所有headview的View。我们可以在重写ListView的addHeaderView(View v)方法,在用户利用addHeaderView(View v)添加headview时,我们顺便将它加入到我们的mHeadViews数组中。
- private ArrayList<View> mHeadViews = new ArrayList<View>();
- @Override
- public void addHeaderView(View v) {
- super.addHeaderView(v);
- if (v != null) {
- mHeadViews.add(v);
- }
- }
- @Override
- public void addHeaderView(View v, Object data, boolean isSelectable) {
- super.addHeaderView(v, data, isSelectable);
- if (v != null) {
- mHeadViews.add(v);
- }
- }
(2)没有完全滚出所有headview
在没有完全滚出所有Headview时,滚动高度就是已经滚动过去的所有headview的高度和加上当前可见的headview滚动过去的高度。代码如下:下面会细讲
- public int getScrollHeight(ListView list, ArrayList<? extends View> headviews) {
- if (list == null || headviews == null){
- return -1;
- }
- int headCount = list.getHeaderViewsCount();
- int firstVisiblePos = list.getFirstVisiblePosition();
- int scrollHeight = 0;
- if (firstVisiblePos < headCount) {
- //如果还在header内部,说明只需要逐个计算header的高度就好了。
- if (headviews.size() == 0) {
- new Exception("内部含有headerView,请在函数入口处设置headview list");
- return -1;
- }
- for (int i = 0; i <= firstVisiblePos; i++) {
- View view = headviews.get(i);
- if (view != null && i == firstVisiblePos) {
- //注意,getTop()是负值,因为已经滚到不可见区域去了
- scrollHeight += (-view.getTop());
- } else if (i != firstVisiblePos) {
- scrollHeight += view.getHeight();
- }
- }
- } else {
- //完全滚出headview
- …………
- }
- return scrollHeight;
- }
- int headCount = list.getHeaderViewsCount();
- int firstVisiblePos = list.getFirstVisiblePosition();
- int scrollHeight = 0;
- if (firstVisiblePos < headCount) {
- //没有完全滚出Headview
- }else{
- //完全滚出Headview
- }
下面是计算滚动高度的代码了。
- for (int i = 0; i <= firstVisiblePos; i++) {
- View view = headviews.get(i);
- if (view != null && i == firstVisiblePos) {
- //注意,getTop()是负值,因为已经滚到不可见区域去了
- scrollHeight += (-view.getTop());
- } else if (i != firstVisiblePos) {
- scrollHeight += view.getHeight();
- }
- }
- if (i != firstVisiblePos) {
- scrollHeight += view.getHeight();
- }
- if (view != null && i == firstVisiblePos) {
- //注意,getTop()是负值,因为已经滚到不可见区域去了
- scrollHeight += (-view.getTop());
- }
(3)、完全滚出所有headview
下面就来看看当滚出所有Headview时的代码计算方法:
- public int getScrollHeight(ListView list, ArrayList<? extends View> headviews) {
- …………
- if (firstVisiblePos < headCount) {
- //如果还在header内部,说明只需要逐个计算header的高度就好了。
- } else {
- //先得到所有headview的高度和
- if (headviews != null) {
- for (View view : headviews) {
- scrollHeight += view.getHeight();
- }
- }
- //获取单个item的视图
- View itemView = list.getChildAt(0);
- if (itemView != null) {
- //这里计算的是从headview到当前可见的item之间已经被完全滚过去的item的总高度
- scrollHeight += (firstVisiblePos - headCount) * itemView.getHeight();
- }
- //最后加上当前可见的item,已经滚动的部分
- scrollHeight += (-itemView.getTop());
- }
- return scrollHeight;
- }
首先,得到所有Headview的高度和:
- if (headviews != null) {
- for (View view : headviews) {
- scrollHeight += view.getHeight();
- }
- }
- View itemView = list.getChildAt(0);
- if (itemView != null) {
- //这里计算的是从headview到当前可见的item之间已经被完全滚过去的item的总高度
- scrollHeight += (firstVisiblePos - headCount) * itemView.getHeight();
- }
- scrollHeight += (-itemView.getTop());
- public int getScrollHeight(ListView list, ArrayList<? extends View> headviews) {
- if (list == null || headviews == null){
- return -1;
- }
- //!!!注意!!!
- //这里使用list.getHeaderViewsCount();获取到的headview数量
- int headCount = list.getHeaderViewsCount();
- int firstVisiblePos = list.getFirstVisiblePosition();
- int scrollHeight = 0;
- if (firstVisiblePos < headCount) {
- //如果还在header内部,说明只需要逐个计算header的高度就好了。
- if (headviews.size() == 0) {
- new Exception("内部含有headerView,请在函数入口处设置headview list");
- return -1;
- }
- for (int i = 0; i <= firstVisiblePos; i++) {
- View view = headviews.get(i);
- if (view != null && i == firstVisiblePos) {
- //注意,getTop()是负值,因为已经滚到不可见区域去了
- scrollHeight += (-view.getTop());
- } else if (i != firstVisiblePos) {
- scrollHeight += view.getHeight();
- }
- }
- } else {
- //这里假设除了headview以外的所有的正常ListItem的高度都是一样的。如果你的不一样,需要改写这一部分的计算方式了
- //已经滚出所以headView,只需要将所以headview高度相加,然后再加上其它所有list的高度即可
- if (headviews != null) {
- for (View view : headviews) {
- scrollHeight += view.getHeight();
- }
- }
- //获取单个item的视图
- View itemView = list.getChildAt(0);
- //值得非常注意的是firstVisiblePos是从0开始算的,所以headCount正好对应listview的第一个item的索引
- if (itemView != null) {
- //这里计算的是从headview到当前可见的item之间已经被完全滚过去的item的总高度
- scrollHeight += (firstVisiblePos - headCount) * itemView.getHeight();
- }
- //最后加上当前可见的item,已经滚动的部分
- scrollHeight += (-itemView.getTop());
- }
- return scrollHeight;
- }
4、下拉
在费了这么大劲讲解了getScrollHeight()的方法后,下面说说下拉的代码。在onTouchEvent()中捕捉ACTION_MOVE事件,当满足条件时就使用layout(left,top,right,bottom)函数来移动当前布局。
代码如下,后面会细讲。
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_MOVE: {
- int moveHeight = (int) event.getY() - mTouchPoint.y;
- int scrolledHeight = getScrollHeight(this, mHeadViews);;
- if (moveHeight > 0 && scrolledHeight == 0) {
- if (moveHeight > mContentMaxMoveHeight){
- moveHeight = mContentMaxMoveHeight;
- }
- float headerMoveHeight = moveHeight * 0.5f * SCROLL_RATIO;
- float contentMoveHeight = moveHeight * SCROLL_RATIO;
- mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight);
- mContentTop = (int) (mContentInitRect.top + contentMoveHeight);
- mTopView.layout(mHeadInitRect.left, mHeaderCurTop, mHeadInitRect.right, (int) (mHeadInitRect.bottom + headerMoveHeight));
- mContentView.layout(mContentInitRect.left, mContentTop, mContentInitRect.right, (int) (mContentInitRect.bottom + contentMoveHeight));
- mIsMoving = true;
- mEnableTouch = true;
- } else {
- mEnableTouch = false;
- }
- }
- break;
- …………
- return mEnableTouch || super.onTouchEvent(event);
- }
- int moveHeight = (int) event.getY() - mTouchPoint.y;
- int scrolledHeight = getScrollHeight(this, mHeadViews);;
- if (moveHeight > 0 && scrolledHeight == 0) {
- }
因为我们设置了最大移动距离,所以当移动距离超过最大移动距离时,就将其始终赋值为最大移动距离。
- if (moveHeight > mContentMaxMoveHeight){
- moveHeight = mContentMaxMoveHeight;
- }
- float headerMoveHeight = moveHeight * 0.5f * SCROLL_RATIO;
- float contentMoveHeight = moveHeight * SCROLL_RATIO;
- mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight);
- mContentTop = (int) (mContentInitRect.top + contentMoveHeight);
- mTopView.layout(mHeadInitRect.left, mHeaderCurTop, mHeadInitRect.right, (int) (mHeadInitRect.bottom + headerMoveHeight));
- mContentView.layout(mContentInitRect.left, mContentTop, mContentInitRect.right, (int) (mContentInitRect.bottom + contentMoveHeight));
5、回弹
在下拉之后,当ACTION_UP时,要回弹的初始化位置。代码如下:
- case MotionEvent.ACTION_UP: {
- //反弹
- if (mIsMoving) {
- mTopView.layout(mHeadInitRect.left, mHeadInitRect.top, mHeadInitRect.right, mHeadInitRect.bottom);
- TranslateAnimation headAnim = new TranslateAnimation(0, 0, mHeaderCurTop - mHeadInitRect.top, 0);
- headAnim.setDuration(200);
- mTopView.startAnimation(headAnim);
- mContentView.layout(mContentInitRect.left, mContentInitRect.top, mContentInitRect.right, mContentInitRect.bottom);
- TranslateAnimation contentAnim = new TranslateAnimation(0, 0, mContentTop - mContentInitRect.top, 0);
- contentAnim.setDuration(200);
- mContentView.startAnimation(contentAnim);
- mIsMoving = false;
- }
- mEnableTouch = false;
- }
- break;
这部分,涉及到layout()的应用,及与Animation的结合的方法。看起来这只有几行代码,但真正理解出来还是比较有难度的,我在第二篇博客中详细讲解了,大家可以去看《PullScrollView详解(二)——Animation、Layout与下拉回弹》 大家看了之后,这里就不会有什么问题了。就不再细讲了。
好了,到这里所有代码都讲完了
**源码在文章底部给出**
三、延伸拓展
1、ViewHelper.setTranslationY()——完美的滚动方案
我们博客中所有用到滚动的地方都用的layout()函数来实现的,但当布局层级比较复杂的时候,layout()会失效。这里向大家一个能够完美实现滚动的类:ViewHelper;它是nineoldandroids.jar包里的类。
NineOldAndroids源码地址: https://github.com/JakeWharton/NineOldAndroids
这个类能实现有关动画的很多功能,而且出错率很小,我们项目中也一直在用。
这里我们用到下面的函数,意义是将指定View在指定Y轴上移动指定距离。
- public static void setTranslationY(View view, float translationY)
比如我们先执行ViewHelper.setTranslationY(view,200);然后再执行ViewHelper.setTranslationY(view,0);那它的位置在哪呢?
比如我们原来的位置在A,然后执行setTranslationY(view,200)时View向下移动200像素到B,当执行.setTranslationY(view,0)时View会再回到A点!!!!
这说明,setTranslationY()的坐标原点始终是不会变的!!!!其实从函数名也比较好理解,Translation表示滚动。setTranslationY(view,200)表示沿Y轴向下滚动到200像素的位置。注意是滚动到XXX位置,而不是向下滚动XXX像素,他的意义是scrollTo()而不是scrollBy(),setTranslationY(view,translationY)中的translationY代表的是目的Y坐标!!!!
讲到这,大家应该都理解了,下面我们就将我们下拉和反弹部分替换成ViewHelper.setTranslationY(),代码如下:下面的代码中只是把layout()换成了setTranslationY(),其它都没变,这里就不再讲了。
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_MOVE: {
- int moveHeight = (int) event.getY() - mTouchPoint.y;
- int scrolledHeight = getScrollHeight(this, mHeadViews);
- if (moveHeight > 0 && scrolledHeight == 0) {
- if (moveHeight > mContentMaxMoveHeight){
- moveHeight = mContentMaxMoveHeight;
- }
- float headerMoveHeight = moveHeight * 0.5f * SCROLL_RATIO;
- float contentMoveHeight = moveHeight * SCROLL_RATIO;
- mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight);
- mContentTop = (int) (mContentInitRect.top + contentMoveHeight);
- //viewHelper是导入的jar包,在有些情况下layout()函数实现的并不好使,源自NineOldAndroids开源项目
- ViewHelper.setTranslationY(mTopView, headerMoveHeight);
- ViewHelper.setTranslationY(mContentView, contentMoveHeight);
- mIsMoving = true;
- mEnableTouch = true;
- } else {
- mEnableTouch = false;
- }
- }
- break;
- case MotionEvent.ACTION_UP: {
- //反弹
- if (mIsMoving) {
- ViewHelper.setTranslationY(mTopView, 0);
- TranslateAnimation headAnim = new TranslateAnimation(0, 0, mHeaderCurTop - mHeadInitRect.top, 0);
- headAnim.setDuration(200);
- mTopView.startAnimation(headAnim);
- ViewHelper.setTranslationY(mContentView, 0);
- TranslateAnimation contentAnim = new TranslateAnimation(0, 0, mContentTop - mContentInitRect.top, 0);
- contentAnim.setDuration(200);
- mContentView.startAnimation(contentAnim);
- mIsMoving = false;
- }
- mEnableTouch = false;
- }
- break;
- case MotionEvent.ACTION_CANCEL: {
- mEnableTouch = false;
- }
- break;
- }
- // 禁止控件本身的滑动.
- //这句厉害,如果mEnableMoving返回TRUE,那么就不会执行super.onTouchEvent(event)
- //只有返回FALSE的时候,才会执行super.onTouchEvent(event)
- //禁止控件本身的滑动,就会让它,本来应有的滑动就不会滑动了,比如向上滚动
- //!!!!!这点对于listview控件尤为重要。因为在上滑时,如果不禁止控件本身的向上移动,
- // 就会乱套,因为你本不需要利用setTranslationY()上移的地方,他仍然会上移
- return mEnableTouch || super.onTouchEvent(event);
- }
- 1、基本框架:第一部分,搭框架对应源码
- 2、最终代码:本文最终的源代码
如果本文有帮到你,记得加关注哦
源码下载地址:http://download.csdn.net/detail/harvic880925/9062013