如图所示是效果图,当向下拉时,图片会被拉出来,松手后恢复。和ListView的侧滑删除
1.视差特效
首先图片是通过addHeaderView加上去的,所以在设置Adapter前先设置一个View来作为头布局,图片的设置android:scaleType="centerCrop"
然后可以重写ListView主要是用通过overScrollBy来实现图片的拉出效果的,当拉出图片绘制后,重写onTouchEvent方法MotionEvent.ACTION_UP
实现回弹
如下是主代码
1 package com.demo.sb.main; 2 3 import com.demo.sb.adapter.ParallacAdapter; 4 import com.demo.sb.widget.MyParallaxListView; 5 import com.demo.suibian.R; 6 7 import android.app.Activity; 8 import android.os.Bundle; 9 import android.view.View;10 import android.view.Window;11 import android.view.ViewTreeObserver.OnGlobalLayoutListener;12 import android.widget.ImageView;13 14 /**15 * 视差特效类型于QQ空间上的16 * 17 * @author Administrator18 * 19 */20 public class MActivity_Parallac extends Activity {21 22 private MyParallaxListView mListView;23 24 @Override25 protected void onCreate(Bundle savedInstanceState) {26 // TODO Auto-generated method stub27 super.onCreate(savedInstanceState);28 requestWindowFeature(Window.FEATURE_NO_TITLE);29 setContentView(R.layout.mactivity_parallax);30 31 mListView = (MyParallaxListView) findViewById(R.id.lv);32 mListView.setOverScrollMode(View.OVER_SCROLL_NEVER);33 // 加上Header34 final View mHeaderView = View.inflate(MActivity_Parallac.this,35 R.layout.viewheader, null);36 final ImageView mImage = (ImageView) mHeaderView.findViewById(R.id.iv);37 mListView.addHeaderView(mHeaderView);38 39 mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(40 new OnGlobalLayoutListener() {41 42 @Override43 public void onGlobalLayout() {44 // TODO Auto-generated method stub45 mListView.setParallaxImage(mImage);46 47 mHeaderView.getViewTreeObserver()48 .removeGlobalOnLayoutListener(this);49 }50 });51 52 // mListView.setAdapter(new SuibianAdapter(this));53 mListView.setAdapter(new ParallacAdapter(this));54 }55 }
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="200dp" android:contentDescription="@null" android:scaleType="centerCrop" android:src="@drawable/d" /></LinearLayout>
重写的ListView
1 package com.demo.sb.widget; 2 3 import com.nineoldandroids.animation.ValueAnimator; 4 import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener; 5 6 import android.content.Context; 7 import android.util.AttributeSet; 8 import android.util.Log; 9 import android.view.MotionEvent; 10 import android.view.animation.Animation; 11 import android.view.animation.OvershootInterpolator; 12 import android.view.animation.Transformation; 13 import android.widget.ImageView; 14 import android.widget.ListView; 15 16 public class MyParallaxListView extends ListView{ 17 18 private ImageView mImage; 19 private int drawableHeight; 20 private int mOriginalHeight; 21 public MyParallaxListView(Context context) { 22 super(context); 23 // TODO Auto-generated constructor stub 24 } 25 26 public MyParallaxListView(Context context, AttributeSet attrs) { 27 super(context, attrs); 28 // TODO Auto-generated constructor stub 29 } 30 31 public MyParallaxListView(Context context, AttributeSet attrs, int defStyle) { 32 super(context, attrs, defStyle); 33 // TODO Auto-generated constructor stub 34 } 35 36 /** 37 * 设置ImageView图片,拿到引用 38 * @param mImage 39 */ 40 public void setParallaxImage(ImageView mImage) { 41 // TODO Auto-generated method stub 42 this.mImage = mImage; 43 mOriginalHeight = mImage.getHeight(); 44 drawableHeight = mImage.getDrawable().getIntrinsicHeight(); 45 46 Log.d("jiejie", "height: " + mOriginalHeight + " drawableHeight: " + drawableHeight); 47 } 48 49 @Override 50 protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, 51 int scrollY, int scrollRangeX, int scrollRangeY, 52 int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 53 // TODO Auto-generated method stub 54 // deltaY : 竖直方向的瞬时偏移量 / 变化量 dx 顶部到头下拉为-, 底部到头上拉为+ 55 // scrollY : 竖直方向的偏移量 / 变化量 56 // scrollRangeY : 竖直方向滑动的范围 57 // maxOverScrollY : 竖直方向最大滑动范围 58 // isTouchEvent : 是否是手指触摸滑动, true为手指, false为惯性 59 60 Log.d("jiejie", "deltaY: " +deltaY + " scrollY: " + scrollY + " scrollRangeY: " + scrollRangeY 61 + " maxOverScrollY: " + maxOverScrollY + " isTouchEvent: " + isTouchEvent); 62 //手指拉动并且是下拉 63 if(isTouchEvent && deltaY <0){ 64 //把拉动的瞬时变化量的绝对值交给Header,就可以实现放大的效果了 65 if(mImage.getHeight() <=drawableHeight ){ 66 int newHeight = (int)(mImage.getHeight() + Math.abs(deltaY / 3.0f)); 67 //高度不超过图片最大高度时,才让其生效 68 mImage.getLayoutParams().height = newHeight; 69 mImage.requestLayout(); 70 } 71 } 72 73 return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, 74 scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 75 } 76 77 @Override 78 public boolean onTouchEvent(MotionEvent ev) { 79 // TODO Auto-generated method stub 80 81 switch (ev.getAction()) { 82 case MotionEvent.ACTION_UP: 83 //执行回弹动画,方式一:属性动画 84 //从当前高度mImage.getHeight(),执行动画到原始高度mOriginalHeight 85 final int startHeight = mImage.getHeight(); 86 final int endHeight = mOriginalHeight; 87 //ValueAnimator(startHeight,endHeight); 88 89 90 //执行回弹动画,方式二:自定义Animation 91 ResetAnimation animation = new ResetAnimation(mImage,startHeight,endHeight); 92 startAnimation(animation); 93 94 break; 95 96 default: 97 break; 98 } 99 return super.onTouchEvent(ev);100 }101 102 private void ValueAnimator(final int startHeight, final int endHeight) {103 // TODO Auto-generated method stub104 ValueAnimator mValueAnimator = ValueAnimator.ofInt(1);105 mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {106 107 @Override108 public void onAnimationUpdate(ValueAnimator arg0) {109 // TODO Auto-generated method stub110 float fraction = arg0.getAnimatedFraction();111 //percent 0.0-1.0112 Integer newHeight = evaluate(fraction, startHeight, endHeight);113 114 mImage.getLayoutParams().height = newHeight;115 mImage.requestLayout();116 }117 });118 mValueAnimator.setInterpolator(new OvershootInterpolator());119 mValueAnimator.setDuration(800);120 mValueAnimator.start();121 }122 /**123 * 类型估值器124 * @param fraction125 * @param startValue126 * @param endValue127 * @return128 */129 public Integer evaluate(float fraction, Integer startValue, Integer endValue) {130 int startInt = startValue;131 return (int)(startInt + fraction * (endValue - startInt));132 }133 134 /**135 * 执行的动画136 * @author Administrator137 *138 */139 private class ResetAnimation extends Animation{140 141 private final ImageView mImageView;142 private final int startHeight;143 private final int endHeight;144 145 public ResetAnimation(ImageView mImage, int startHeight, int endHeight) {146 // TODO Auto-generated constructor stub147 this.mImageView = mImage;148 this.startHeight = startHeight;149 this.endHeight = endHeight;150 /**151 * Interpolator被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速)152 * decelerated(减速),repeated(重复),bounced(弹跳)等153 * 154 * OvershootInterpolator 向前甩一定值后再回到原来位置155 */156 setInterpolator(new OvershootInterpolator());157 //设置动画的执行时长158 setDuration(800);159 }160 @Override161 protected void applyTransformation(float interpolatedTime,162 Transformation t) {163 // TODO Auto-generated method stub164 System.out.println(interpolatedTime );165 //interpolatedTime 0.0f -> 1.0f166 Integer newHeightInteger = evaluate(interpolatedTime, startHeight, endHeight);167 168 mImageView.getLayoutParams().height = newHeightInteger;169 mImageView.requestLayout();170 super.applyTransformation(interpolatedTime, t);171 }172 }173 }
2.实现ListView的侧滑删除功能
自定义的布局来显示侧滑的功能
1 package com.demo.sb.adapter; 2 3 import com.demo.sb.utils.DensityUtil; 4 5 import android.content.Context; 6 import android.graphics.Rect; 7 import android.support.v4.view.ViewCompat; 8 import android.support.v4.widget.ViewDragHelper; 9 import android.util.AttributeSet; 10 import android.view.MotionEvent; 11 import android.view.View; 12 import android.widget.FrameLayout; 13 14 /** 15 * 侧拉删除控件 16 * 17 * @author Administrator 18 * 19 */ 20 public class SwipeLayout extends FrameLayout { 21 22 private Status status = Status.Close; 23 private OnSwipeLayoutListener swipeLayoutListener; 24 25 public Status getStatus() { 26 return status; 27 } 28 29 public void setStatus(Status status) { 30 this.status = status; 31 } 32 33 public OnSwipeLayoutListener getSwipeLayoutListener() { 34 return swipeLayoutListener; 35 } 36 37 public void setSwipeLayoutListener(OnSwipeLayoutListener swipeLayoutListener) { 38 this.swipeLayoutListener = swipeLayoutListener; 39 } 40 41 public static enum Status { 42 Close, Open, Draging 43 } 44 45 public static interface OnSwipeLayoutListener { 46 void onClose(SwipeLayout mSwipeLayout); 47 48 void onOpen(SwipeLayout mSwipeLayout); 49 50 void onDraging(SwipeLayout mSwipeLayout); 51 52 // 要去关闭 53 void onStartClose(SwipeLayout mSwipeLayout); 54 55 // 要去开启 56 void onStartOpen(SwipeLayout mSwipeLayout); 57 } 58 59 public SwipeLayout(Context context) { 60 this(context, null); 61 // TODO Auto-generated constructor stub 62 } 63 64 public SwipeLayout(Context context, AttributeSet attrs) { 65 this(context, attrs, 0); 66 // TODO Auto-generated constructor stub 67 } 68 69 public SwipeLayout(Context context, AttributeSet attrs, int defStyle) { 70 super(context, attrs, defStyle); 71 // TODO Auto-generated constructor stub 72 mDragHelper = ViewDragHelper.create(this, 1.0f, mCallback); 73 } 74 75 ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { 76 // c . 重写监听 77 @Override 78 public boolean tryCaptureView(View arg0, int arg1) { 79 // TODO Auto-generated method stub 80 return true; 81 } 82 83 // 限定移动范围 84 public int clampViewPositionHorizontal(View child, int left, int dx) { 85 // left 86 if (child == mFrontView) { 87 if (left > 0) { 88 return 0; 89 } else if (left < -mRange) { 90 return -mRange; 91 } 92 } else if (child == mBackView) { 93 if (left > mWidth) { 94 return mWidth; 95 } else if (left < mWidth - mRange) { 96 return mWidth - mRange; 97 } 98 } 99 return left;100 };101 102 public void onViewPositionChanged(View changedView, int left, int top,103 int dx, int dy) {104 // 专递事件105 if (changedView == mFrontView) {106 mBackView.offsetLeftAndRight(dx);107 } else if (changedView == mBackView) {108 mFrontView.offsetLeftAndRight(dx);109 }110 despatchSwipeEvent();111 112 // 兼容老版本113 invalidate();114 115 };116 117 public void onViewReleased(View releasedChild, float xvel, float yvel) {118 if (xvel == 0 && mFrontView.getLeft() < -mRange / 2.0f) {119 open();120 } else if (xvel < 0) {121 open();122 } else {123 close();124 }125 };126 127 };128 129 private ViewDragHelper mDragHelper;130 private View mBackView;131 private View mFrontView;132 private int mHeight;133 private int mWidth;134 private int mRange;135 136 // b. 专递触摸事件137 @Override138 public boolean onInterceptTouchEvent(MotionEvent ev) {139 // TODO Auto-generated method stub140 return mDragHelper.shouldInterceptTouchEvent(ev);141 }142 143 protected void despatchSwipeEvent() {144 // TODO Auto-generated method stub145 if (swipeLayoutListener != null) {146 swipeLayoutListener.onDraging(this);147 }148 // 记录上一次的状态149 Status preStatus = status;150 // 更新当前的状态151 status = updateStatus();152 if (preStatus != status && swipeLayoutListener != null) {153 if (status == Status.Close) {154 swipeLayoutListener.onClose(this);155 } else if (status == Status.Open) {156 swipeLayoutListener.onOpen(this);157 } else if (status == Status.Draging) {158 if (preStatus == Status.Close) {159 swipeLayoutListener.onStartOpen(this);160 } else if (preStatus == Status.Open) {161 swipeLayoutListener.onStartClose(this);162 }163 }164 }165 }166 167 private Status updateStatus() {168 169 int left = mFrontView.getLeft();170 if (left == 0) {171 return Status.Close;172 } else if (left == -mRange) {173 return Status.Open;174 }175 return Status.Draging;176 }177 178 public void close() {179 // TODO Auto-generated method stub180 DensityUtil.showToast(getContext(), "Close");181 close(true);182 }183 184 public void close(boolean isSmooth) {185 // TODO Auto-generated method stub186 int finalLeft = 0;187 if (isSmooth) {188 // 开始动画189 if (mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {190 ViewCompat.postInvalidateOnAnimation(this);191 }192 } else {193 layoutContent(false);194 }195 }196 197 public void open() {198 DensityUtil.showToast(getContext(), "Open");199 open(true);200 }201 202 public void open(boolean isSmooth) {203 int finalLeft = -mRange;204 if (isSmooth) {205 // 开始动画206 if (mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {207 ViewCompat.postInvalidateOnAnimation(this);208 }209 } else {210 layoutContent(true);211 }212 }213 214 @Override215 public void computeScroll() {216 // TODO Auto-generated method stub217 super.computeScroll();218 219 if (mDragHelper.continueSettling(true)) {220 ViewCompat.postInvalidateOnAnimation(this);221 }222 }223 224 @Override225 public boolean onTouchEvent(MotionEvent event) {226 // TODO Auto-generated method stub227 try {228 mDragHelper.processTouchEvent(event);229 } catch (Exception e) {230 e.printStackTrace();231 }232 return true;233 }234 235 @Override236 protected void onLayout(boolean changed, int left, int top, int right,237 int bottom) {238 // TODO Auto-generated method stub239 super.onLayout(changed, left, top, right, bottom);240 // 摆放位置241 layoutContent(false);242 }243 244 private void layoutContent(boolean isOpen) {245 // TODO Auto-generated method stub246 // 摆放钱View247 Rect frontRect = computeFrontViewRect(isOpen);248 mFrontView.layout(frontRect.left, frontRect.top, frontRect.right,249 frontRect.bottom);250 // 摆放后View251 Rect backRect = computeBackViewViaFront(frontRect);252 mBackView.layout(backRect.left, backRect.top, backRect.right,253 backRect.bottom);254 255 // 调整顺序,把mFrontView前置256 bringChildToFront(mFrontView);257 }258 259 private Rect computeBackViewViaFront(Rect frontRect) {260 // TODO Auto-generated method stub261 int left = frontRect.right;262 return new Rect(left, 0, left + mRange, 0 + mHeight);263 }264 265 private Rect computeFrontViewRect(boolean isOpen) {266 // TODO Auto-generated method stub267 int left = 0;268 if (isOpen) {269 left = -mRange;270 }271 return new Rect(left, 0, left + mWidth, 0 + mHeight);272 }273 274 @Override275 protected void onFinishInflate() {276 // TODO Auto-generated method stub277 super.onFinishInflate();278 mBackView = getChildAt(0);279 mFrontView = getChildAt(1);280 }281 282 @Override283 protected void onSizeChanged(int w, int h, int oldw, int oldh) {284 // TODO Auto-generated method stub285 super.onSizeChanged(w, h, oldw, oldh);286 mHeight = mFrontView.getMeasuredHeight();287 mWidth = mFrontView.getMeasuredWidth();288 289 mRange = mBackView.getMeasuredWidth();290 }291 292 }
1 package com.demo.sb.adapter; 2 3 import java.util.ArrayList; 4 5 import com.demo.sb.adapter.SwipeLayout.OnSwipeLayoutListener; 6 import com.demo.sb.utils.DensityUtil; 7 import com.demo.suibian.R; 8 9 import android.app.AlertDialog; 10 import android.content.Context; 11 import android.content.DialogInterface; 12 import android.view.View; 13 import android.view.ViewGroup; 14 import android.widget.BaseAdapter; 15 import android.widget.TextView; 16 17 public class ParallacAdapter extends BaseAdapter { 18 19 private Context context; 20 private int[] cost = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; 21 private String[] nameStrings = { "宋江", "卢俊义", "吴用", "公孙胜", "关胜", "林冲", 22 "秦明", "呼延灼", "花荣", "柴进", "李应", "鲁智深", "索超", "戴宗" }; 23 private ArrayList<SwipeLayout> opendItems; 24 25 public ParallacAdapter(Context context) { 26 super(); 27 this.context = context; 28 29 opendItems = new ArrayList<SwipeLayout>(); 30 } 31 32 @Override 33 public int getCount() { 34 // TODO Auto-generated method stub 35 return nameStrings.length; 36 } 37 38 @Override 39 public Object getItem(int arg0) { 40 // TODO Auto-generated method stub 41 return nameStrings[arg0]; 42 } 43 44 @Override 45 public long getItemId(int arg0) { 46 // TODO Auto-generated method stub 47 return arg0; 48 } 49 50 @Override 51 public View getView(final int arg0, View arg1, ViewGroup arg2) { 52 // TODO Auto-generated method stub 53 ViewHolder holder; 54 if (arg1 == null) { 55 holder = new ViewHolder(); 56 arg1 = View.inflate(context, R.layout.item_parallac_list, null); 57 holder.tv_del = (TextView) arg1.findViewById(R.id.tv_item_pardel); 58 holder.tv_name = (TextView) arg1 59 .findViewById(R.id.item_parallax_name); 60 holder.tv_fight = (TextView) arg1 61 .findViewById(R.id.item_parallax_fight); 62 holder.tv_call = (TextView) arg1.findViewById(R.id.tv_item_parcall); 63 arg1.setTag(holder); 64 } else { 65 holder = (ViewHolder) arg1.getTag(); 66 } 67 68 SwipeLayout sLayout = (SwipeLayout) arg1; 69 sLayout.setSwipeLayoutListener(new OnSwipeLayoutListener() { 70 71 @Override 72 public void onStartOpen(SwipeLayout mSwipeLayout) { 73 // TODO Auto-generated method stub 74 // 要去开启时,先遍历所有已打开的条目,逐个关闭 75 for (SwipeLayout layout : opendItems) { 76 layout.close(); 77 } 78 opendItems.clear(); 79 } 80 81 @Override 82 public void onStartClose(SwipeLayout mSwipeLayout) { 83 // TODO Auto-generated method stub 84 85 } 86 87 @Override 88 public void onOpen(SwipeLayout mSwipeLayout) { 89 // TODO Auto-generated method stub 90 opendItems.add(mSwipeLayout); 91 } 92 93 @Override 94 public void onDraging(SwipeLayout mSwipeLayout) { 95 // TODO Auto-generated method stub 96 97 } 98 99 @Override100 public void onClose(SwipeLayout mSwipeLayout) {101 // TODO Auto-generated method stub102 opendItems.remove(mSwipeLayout);103 }104 });105 holder.tv_name.setText("名字为 :" + nameStrings[arg0]);106 holder.tv_fight.setText("战斗力: " + cost[arg0]);107 holder.tv_del.setOnClickListener(new View.OnClickListener() {108 109 @Override110 public void onClick(View view) {111 // TODO Auto-generated method stub112 DensityUtil.showToast(context, "第" + arg0 + "个 为"113 + nameStrings[arg0]);114 }115 });116 holder.tv_call.setOnClickListener(new View.OnClickListener() {117 118 @Override119 public void onClick(View view) {120 // TODO Auto-generated method stub121 AlertDialog.Builder builder = new AlertDialog.Builder(context);122 builder.setTitle("标题!!!");123 builder.setCancelable(false);124 builder.setMessage("设置警告,是否确定删除,删除是不可逆的奥");125 builder.setPositiveButton("确定",126 new DialogInterface.OnClickListener() {127 128 @Override129 public void onClick(DialogInterface perent, int arg1) {130 // TODO Auto-generated method stub131 DensityUtil.showToast(context,132 nameStrings[arg0]);133 }134 });135 builder.setNegativeButton("取消",136 new DialogInterface.OnClickListener() {137 138 @Override139 public void onClick(DialogInterface arg0, int arg1) {140 // TODO Auto-generated method stub141 142 }143 });144 builder.create().show();145 }146 });147 return arg1;148 }149 150 static class ViewHolder {151 TextView tv_del;152 TextView tv_name;153 TextView tv_fight;154 TextView tv_call;155 }156 }
<?xml version="1.0" encoding="utf-8"?><com.demo.sb.adapter.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/sl" android:layout_width="match_parent" android:layout_height="80dp" android:background="#4000" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/tv_item_parcall" android:layout_width="100dp" android:layout_height="match_parent" android:background="#666" android:gravity="center" android:text="call" android:textColor="#fff" /> <TextView android:id="@+id/tv_item_pardel" android:layout_width="100dp" android:layout_height="match_parent" android:background="#f00" android:gravity="center" android:text="Delete" android:textColor="#fff" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#4fff" android:padding="5dp" > <ImageView android:id="@+id/item_view" android:layout_width="80dp" android:layout_height="80dp" android:contentDescription="@null" android:scaleType="fitXY" android:src="@drawable/a" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" > <TextView android:id="@+id/item_parallax_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#f00" android:textSize="20sp" /> <TextView android:id="@+id/item_parallax_fight" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="10dp" android:text="" android:textColor="#5000" android:textSize="16sp" /> </LinearLayout> </LinearLayout></com.demo.sb.adapter.SwipeLayout>