先来看一下效果:
scrollzoom_listview.gif
一、设计思路与实现步骤
1、本例是通过重写ListView来实现的,头部的图片是ListView的HeadView。定义一个headview.xml布局文件,在这个布局文件中放一个ImageView,并给IamgeView设置一个初始高度
2、实现下拉图片放大
当ListView处于顶部的时候下拉实现图片放大,这里要用的一个核心的方法是overScrollBy方法,当View的滑动超过了正常内容的边界时,该方法被调用。这里也许我解释的不是非常完美,大家可以自己去看一下该方法的注释
/**
* Scroll the view with standard behavior for scrolling beyond the normal
* content boundaries. Views that call this method should override
* {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
* results of an over -scroll operation.
*
* Views can use this method to handle any touch or fling -based scrolling.
*
* @param deltaX Change in X in pixels
* @param deltaY Change in Y in pixels
* @param scrollX Current X scroll value in pixels before applying deltaX
* @param scrollY Current Y scroll value in pixels before applying deltaY
* @param scrollRangeX Maximum content scroll range along the X axis
* @param scrollRangeY Maximum content scroll range along the Y axis
* @param maxOverScrollX Number of pixels to overscroll by in either direction
* along the X axis.
* @param maxOverScrollY Number of pixels to overscroll by in either direction
* along the Y axis.
* @param isTouchEvent true if this scroll operation is the result of a touch event.
* @return true if scrolling was clamped to an over-scroll boundary along either
* axis, false otherwise.
*/
deltaX表示X轴上过度滑动多出的那部分距离,deltaY表示Y轴上过度滑动多出的那部分距离。那么我们可以得出一个结论:图片的高度=图片原有的高度+|deltaY|,实际上无论是deltaY还是deltaX都是有正负之分的,当ListView处于顶部且的下拉的时候deltaY<0
当ListView处于底部的时候上拉deltaY>0。
3、实现上拉图片缩小恢复
这一步的核心方法是onScrollChanged,不断的监听图片的高度,并且重新绘制界面
图片的高度=图片原有高度-超出屏幕部分的高度
4、实现松手会弹动画
(1)自定义一个动画
(2)重写onTouchEvent方法UP事件的时候启动动画
二、具体实现
1、在重写ListView之前,我们先把准备工作做一下:
MaiinActivity
package lc.com.ui_zoomlistview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ZoomListView zoomListView;
private ImageView imageView;
private List list;
private ItemAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData();
}
private void initData() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
list.add("测试数据" + i);
}
adapter.notifyDataSetChanged();
}
private void initViews() {
// TODO Auto-generated method stub
list = new ArrayList();
adapter = new ItemAdapter(list, this);
zoomListView = (ZoomListView) findViewById(R.id.zoomListView);
// 加载布局文件
View header = View.inflate(MainActivity.this, R.layout.headview,
null);
imageView = (ImageView) header.findViewById(R.id.layout_header_image);
// 将ImageView传递给ListView
zoomListView.setImageView(imageView);
// 给ListView添加头文件
zoomListView.addHeaderView(header);
// 设置适配器
zoomListView.setAdapter(adapter);
}
}
activity_main.xml
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="lc.com.ui_zoomlistview.MainActivity">
android:id="@+id/zoomListView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
ItemAdapter
package lc.com.ui_zoomlistview;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ItemAdapter extends BaseAdapter {
private List list;
private Context mContext;
private LayoutInflater mInflater;
public ItemAdapter(List list, Context mContext) {
super();
this.list = list;
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder mViewHoder = null;
if (convertView == null) {
mViewHoder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
mViewHoder.item = (TextView) convertView.findViewById(R.id.item);
convertView.setTag(mViewHoder);
} else {
mViewHoder = (ViewHolder) convertView.getTag();
}
mViewHoder.item.setText(getItem(position).toString());
return convertView;
}
class ViewHolder {
TextView item;
}
}
item.xml
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
android:id="@+id/item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp" />
很简单不做停留。下面我们开始真正的重头戏,ZoomListView的实现:
2、ZoomListView
package lc.com.ui_zoomlistview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.ImageView;
import android.widget.ListView;
/**
* Created by HP on 2017/4/13.
*/
public class ZoomListView extends ListView {
private static final String TAG = "ZoomListView";
//ImageView的初始的高度
private int mImageViewHeight;
private ImageView mImageView;
public ZoomListView(Context context) {
this(context, null);
}
public ZoomListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 初始化一些必要的条件
*/
private void init() {
//获取mIageView的初始高度
mImageViewHeight = getContext().getResources().getDimensionPixelSize(R.dimen.size_default_height);
Log.e(TAG, "mImageViewHeight:" + mImageViewHeight);
}
/**
* 设置ImageView
*
* @param imageView
*/
public void setImageView(ImageView imageView) {
this.mImageView = imageView;
}
/**
* 步骤2:实现下拉图片大
*
* @param deltaY Y轴上超出的距离
* @param deltaX X轴上超出的距离
*/
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
//当deltaY的时候表示向下过度滑动
if (deltaY < 0) {
Log.i(TAG, "deltaY:" + deltaY);
int imageViewHeight = mImageView.getHeight() - deltaY;
mImageView.getLayoutParams().height = imageViewHeight;
//重新绘制ImageView
mImageView.requestLayout();
}
//当deltaY>0的时候表示上拉过度
else
{
if (mImageView.getHeight() > mImageViewHeight) {
mImageView.getLayoutParams().height = mImageView.getHeight()
- deltaY;
// 重新绘制,或者叫重新摆放
mImageView.requestLayout();
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
/**
* 步骤3:实现图片
* *@param l
* @param t
* @param oldl
* @param oldt
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
//得到mImageView的父布局,这里明显是headview
View parent = (View) mImageView.getParent();
//实际内容的上边界到父布局上边界的距离
int top = parent.getTop();
if (top < 0 && mImageView.getHeight() > mImageViewHeight) {
//计算图片的高度
int imageViewHeight = mImageView.getHeight() + top;
mImageView.getLayoutParams().height = imageViewHeight;
//重新布局
parent.layout(parent.getLeft(), 0, parent.getRight(), parent.getHeight());
mImageView.requestLayout();
}
super.onScrollChanged(l, t, oldl, oldt);
}
/**
* 步骤3:实现松手回弹动画
*
* @param ev
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
//这里我们只需要实现UP事件
if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.w(TAG, "UP");
//情动回弹动画
ResetAnimation animation = new ResetAnimation(mImageView);
animation.setDuration(300);
mImageView.startAnimation(animation);
}
return super.onTouchEvent(ev);
}
/**
* 自定义一个会弹动画
*/
class ResetAnimation extends Animation {
private ImageView imageView;
//高度差
private int dHeight;
//图片拉升以后的高度
private int orginHeight;
public ResetAnimation(ImageView imageView) {
this.imageView = imageView;
orginHeight=this.imageView.getHeight();
dHeight = orginHeight - mImageViewHeight;
Log.w(TAG, "dHeight:" + dHeight);
}
/**
* 这个方法是为一个自定义动画需要重写的方法
*
* @param interpolatedTime 动画时间0~1.0
* @param t
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
Log.w(TAG, "applyTransformation");
Log.w(TAG, "interpolatedTime:" + interpolatedTime);
//设置图片的大小
//计算图片的高度
int imageViewHeight = (int) (orginHeight - dHeight * interpolatedTime);
mImageView.getLayoutParams().height = imageViewHeight;
mImageView.requestLayout();
super.applyTransformation(interpolatedTime, t);
}
}
}