下拉ScrollView伸缩头布局,实现ScrollView回弹效果
2015-12-22 10:48
489 查看
项目中用到了商品详情展示效果,所以立马想到借鉴天猫商品详情界面,看了天猫的详情页面想到了两套解决方案。1,使用LitView 添加header监听listView 的滑动然后根据listView 的滑动距离计算 header应该滑动的距离 和改变header的高度。2,使用ScrollView 代替1中的ListView 监听onTouch事件,动态改变header的高度,按照这个思路也可以实现ScrollView上下拉的回弹效果或者是上下拉刷新,思路都是一样。
由于项目的商品详情返回的数据 并不是一个集合 而且内容不统一所以使用方案2,下面先看看效果图还是图片有说服力。
这里的主要思路是:计算手指下拉滑动的距离然后设置给header布局,当手指松开时在把header的高度修改回原来的高度,这里用到了开源的动画库nineoldandroids(只需要在build引用compile files(‘libs/nineoldandroids-2.4.0.jar’)),在计算手指下拉滑动的距离时候需要判断ScrollView到达顶部的条件
上拉时候 判断ScrollView到达底部后然后的不走和上拉是一样的
下拉的时候 判断header的高度答到一个临界值的时候 打开header布局
下面是ScrollView 的完整代码
下面是布局文件
在Activity中引用
这个里的ViewPager是自定义ViewPager 因为 如果不对ViewPager做处理的话会产生滑动冲突导致ViewPager不能滑动的后果,对ViewPager的处理也比较简单主要是在dispatchTouchEvent事件里重新分发事件
这是ViewPager的 dispatchTouchEvent方法 就是判断了手指滑动的水平距离和竖直距离
如果竖直方向的距离大于水平方向的距离则调用
getParent().requestDisallowInterceptTouchEvent(false);
这个方法的作用就是告诉父布局 可以拦截ViewPager的事件 这是ViewPager的ontouch不起作用
反之 当水平距离大于竖直距离时 则需要
getParent().requestDisallowInterceptTouchEvent(true);
告诉父容器不需要拦截事件 viewPager自己处理事件
在Activity中ViewPager设置的Adapter 是自己封装了一个PagerAdapter 这样写的好处就是省去了大量重复代码 其代码是:
好了到此结束了。 大致能够实现天猫商品详情的界面,当然这里还有需要可以改进的地方比如在上拉的时候 会有一丝丝的卡顿现象 暂时还没有找到解决办法 我想应该是因为手指轻微抖动导致footer的高度不断变化。
通过这个方法可实现很多中上下拉刷新的效果,已经个人中心界面类似天猫的个人中心界面,原理大致思路都是差不多的。
同时也求一款好的Gif截屏工具
由于项目的商品详情返回的数据 并不是一个集合 而且内容不统一所以使用方案2,下面先看看效果图还是图片有说服力。
这里的主要思路是:计算手指下拉滑动的距离然后设置给header布局,当手指松开时在把header的高度修改回原来的高度,这里用到了开源的动画库nineoldandroids(只需要在build引用compile files(‘libs/nineoldandroids-2.4.0.jar’)),在计算手指下拉滑动的距离时候需要判断ScrollView到达顶部的条件
上拉时候 判断ScrollView到达底部后然后的不走和上拉是一样的
下拉的时候 判断header的高度答到一个临界值的时候 打开header布局
下面是ScrollView 的完整代码
package com.app.test.myscrollview; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ScrollView; import com.nineoldandroids.animation.ValueAnimator; /** * Created by Administrator on 2015/12/18. */ public class MyScrollView extends ScrollView { private ViewGroup innerLayout;//ScrololView里的布局 private View headerView;// 头布局 必须在ScrollView里面 private int originalHeight;//头布局原始高度 private float downY;//手指按下的Y坐标 private View emputyView;//空的布局 用于占位符 private View footerView;//底部布局 private boolean isOpen; private boolean isOpening; protected final static float OFFSET_RADIO = 1.8f; // 偏移量 protected final static float OPEN_RADIO = 1.8f; // 打开比例 public MyScrollView(Context context) { this(context, null); } public MyScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } //布局已经加载完成后调用 一些params参数在这里都能取到值了 @Override protected void onFinishInflate() { super.onFinishInflate(); final int childCount = getChildCount(); if (childCount == 1) { innerLayout = (ViewGroup) getChildAt(0); emputyView = new LinearLayout(getContext()); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); emputyView.setLayoutParams(lp); footerView = new LinearLayout(getContext()); footerView.setLayoutParams(lp); innerLayout.addView(emputyView, 0); innerLayout.addView(footerView, innerLayout.getChildCount()); } else { throw new RuntimeException("ScrollView 只能有一个子布局"); } } public void setOpen(boolean open) { isOpen = open; } public void setOpening(boolean opening) { isOpening = opening; } public void setHeaderView(View headerView) { if (headerView != null) { this.headerView = headerView; originalHeight = headerView.getLayoutParams().height; } } public void setOpenViewListener(OpenViewListener openViewListener) { this.openViewListener = openViewListener; } OpenViewListener openViewListener; public interface OpenViewListener { public void openVeiw(View headerVeiw); } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: downY = ev.getRawY(); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float tempY = ev.getRawY(); float delatY = tempY - downY;//手指竖直方向滑动的距离 downY = tempY; float scrollY = getScrollY();//竖直方向 滚动的值 float offset = innerLayout.getMeasuredHeight() - getHeight();//偏移量 if (scrollY == 0 && delatY > 0) {//表示滑动到顶部了 int openOffset = (int) (delatY / OFFSET_RADIO); if (headerView != null) { int afterHeight = upDateViewHeight(headerView, openOffset); if (afterHeight > originalHeight * OPEN_RADIO && openViewListener != null && !isOpen) { //TODO 需要打开 isOpening = true; openViewListener.openVeiw(headerView); } else { setViewHeight(headerView, afterHeight); } } else { int afterHeight = upDateViewHeight(emputyView, openOffset); setViewHeight(emputyView, afterHeight); } } if (scrollY == offset && delatY < 0) {//滑动到底部了 int afterHeight = upDateViewHeight(footerView, (int) -delatY); setViewHeight(footerView, afterHeight); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP://手指弹开 //手指弹开 让布局的高度 从现在的高度变成0 使用动画 也可以使用Scroller 使用动画简单 if (headerView != null && headerView.getHeight() > originalHeight && !isOpening) { closeView(headerView, headerView.getHeight(), originalHeight); } else if (emputyView.getHeight() > 0) { closeView(emputyView, emputyView.getHeight(), 0); } if (footerView.getHeight() > 0) { closeView(footerView, footerView.getHeight(), 0); } break; } return super.onTouchEvent(ev); } public void closeView(final View view, int fromHeight, final int toHeight) { ValueAnimator animator = ValueAnimator.ofInt(fromHeight, toHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int height = (int) valueAnimator.getAnimatedValue(); view.getLayoutParams().height = height; view.setLayoutParams(view.getLayoutParams()); if (view == headerView && height == toHeight) { isOpen = false; isOpening = false; } } }); animator.start(); animator.setDuration(300); } public void openView(final View view, int fromHeight, final int toHeight) { ValueAnimator animator = ValueAnimator.ofInt(fromHeight, toHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int height = (int) valueAnimator.getAnimatedValue(); view.getLayoutParams().height = height; view.setLayoutParams(view.getLayoutParams()); if (height == toHeight && view == headerView) { isOpen = true; isOpening = false; } } }); animator.start(); animator.setDuration(300); } /** * 改变 布局的高度 * * @param view * @param upDateHeight 更新的高度 * @return 改变后的高度 */ public int upDateViewHeight(View view, int upDateHeight) { int nowHeight = view.getLayoutParams().height; int afterHeight = nowHeight + upDateHeight; return afterHeight; } /** * 设置高度 * * @param view * @param afterHeight */ public void setViewHeight(View view, int afterHeight) { view.getLayoutParams().height = afterHeight; view.setLayoutParams(view.getLayoutParams()); } }
下面是布局文件
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <com.app.test.myscrollview.MyScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:fitsSystemWindows="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.app.test.demo.MyViewPager android:id="@+id/header" android:layout_width="match_parent" android:layout_height="190dp" > </com.app.test.demo.MyViewPager> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dp" android:text="@string/text"/> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/goods_sample"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dp" android:text="@string/text"/> </LinearLayout> </com.app.test.myscrollview.MyScrollView> </FrameLayout>
在Activity中引用
package com.app.test; import android.support.v4.view.PagerAdapter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import com.app.test.demo.GListView; import com.app.test.demo.MyViewPager; import com.app.test.myscrollview.BaseViewPgerAdapter; import com.app.test.myscrollview.MyScrollView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { MyViewPager myViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myViewPager = (MyViewPager) findViewById(R.id.header); List<String> list = new ArrayList<>(); list.add(""); list.add(""); list.add(""); list.add(""); myViewPager.setAdapter(new BaseViewPgerAdapter<String>(list, R.layout.item_img) { @Override public void getView(View view, String item, int position) { } }); final MyScrollView myScrollView = (MyScrollView) findViewById(R.id.scrollView); myScrollView.setHeaderView(myViewPager); myScrollView.setOpenViewListener(new MyScrollView.OpenViewListener() { @Override public void openVeiw(View headerVeiw) { myScrollView.openView(headerVeiw, headerVeiw.getHeight(), getScreenHeight()); } }); } /** * 得到屏幕高度 * * @return 高度 */ public int getScreenHeight() { DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int screenHeight = dm.heightPixels; return screenHeight; } }
这个里的ViewPager是自定义ViewPager 因为 如果不对ViewPager做处理的话会产生滑动冲突导致ViewPager不能滑动的后果,对ViewPager的处理也比较简单主要是在dispatchTouchEvent事件里重新分发事件
@Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction(); if (action == MotionEvent.ACTION_DOWN) { downX = tempX = (int) ev.getX(); downY = tempY = (int) ev.getY(); } else if (action == MotionEvent.ACTION_UP) { // currentPage = this.getCurrentItem() + 1; } else if (action == MotionEvent.ACTION_MOVE) { int moveX = (int) ev.getX(); int moveY = (int) ev.getY(); int deltaX = tempX - moveX; int deltaY = tempY - moveY; tempX = moveX; tempY = moveY; if (Math.abs(deltaY) > Math.abs(deltaX)) { getParent().requestDisallowInterceptTouchEvent(false); return super.dispatchTouchEvent(ev); } } getParent().requestDisallowInterceptTouchEvent(true); return super.dispatchTouchEvent(ev); }
这是ViewPager的 dispatchTouchEvent方法 就是判断了手指滑动的水平距离和竖直距离
如果竖直方向的距离大于水平方向的距离则调用
getParent().requestDisallowInterceptTouchEvent(false);
这个方法的作用就是告诉父布局 可以拦截ViewPager的事件 这是ViewPager的ontouch不起作用
反之 当水平距离大于竖直距离时 则需要
getParent().requestDisallowInterceptTouchEvent(true);
告诉父容器不需要拦截事件 viewPager自己处理事件
在Activity中ViewPager设置的Adapter 是自己封装了一个PagerAdapter 这样写的好处就是省去了大量重复代码 其代码是:
package com.app.test.myscrollview; import android.support.v4.view.PagerAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import java.util.List; /** * Created by Administrator on 2015/12/18. */ public abstract class BaseViewPgerAdapter<T> extends PagerAdapter { List<T> datas; int layoutId; public BaseViewPgerAdapter(List<T> datas, int layoutId) { this.datas = datas; this.layoutId = layoutId; } @Override public int getCount() { return datas == null ? 0 : datas.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } public abstract void getView(View view, T item, int position); @Override public Object instantiateItem(ViewGroup container, int position) { View view = LayoutInflater.from(container.getContext()).inflate(layoutId, null); T item = datas.get(position); getView(view, item, position); container.addView(view); return view; } public ImageView setImageViewRec(View view, int imgId, int imageRec) { ImageView img = (ImageView) view.findViewById(imgId); img.setImageResource(imageRec); return img; } }
好了到此结束了。 大致能够实现天猫商品详情的界面,当然这里还有需要可以改进的地方比如在上拉的时候 会有一丝丝的卡顿现象 暂时还没有找到解决办法 我想应该是因为手指轻微抖动导致footer的高度不断变化。
通过这个方法可实现很多中上下拉刷新的效果,已经个人中心界面类似天猫的个人中心界面,原理大致思路都是差不多的。
同时也求一款好的Gif截屏工具
相关文章推荐
- 网络异常那些事
- try ... catch 与 __try ... __except
- Python过滤emoji
- iOS archive(归档)的总结
- Linux中vi使用
- activiti +spring
- effective c++学习笔记
- Oracle 汉字在不同字符集下所占字节
- siteserver cms 3.4.5+iis6.0 漏洞及修复
- ceph 故障解决备忘
- mysql存储过程按月创建表分区 方式一
- C语言全局变量那些事儿
- Commands to help you to Start Using ScaleIO Storage
- 找不到或无法加载已注册的 .Net Framework Data Provider。
- 留言板表情转换类
- 【译】UI设计基础(UI Design Basics)--启动与停止(Starting and Stopping)(五)
- ThinkPHP+swfupload多图上传实例 经典实用的php多图上传
- 如何使用Java执行cmd命令?
- java.net.SocketException: Connection reset 解决方法
- twitter点赞动画详解