你的位置:首页 > 操作系统

[操作系统]QQ视差特效和ListView侧滑删除


如图所示是效果图,当向下拉时,图片会被拉出来,松手后恢复。和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 }

<?  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>

View Code

重写的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 }

<?  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>

View Code