1.阻尼效果listview
public class MyListView extends ListView implements Runnable { private float mLastDownY = 0f; private int mDistance = 0; private int mStep = 10; private boolean mPositive = false; public MyListView (Context context, AttributeSet attrs) { super(context, attrs); } public MyListView (Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyListView (Context context) { super(context); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (mLastDownY == 0f && mDistance == 0) { mLastDownY = event.getY(); return true; } break; case MotionEvent.ACTION_CANCEL: break; case MotionEvent.ACTION_UP: if (mDistance != 0) { mStep = 1; mPositive = (mDistance >= 0); this.post(this); return true; } mLastDownY = 0f; mDistance = 0; break; case MotionEvent.ACTION_MOVE: if (mLastDownY != 0f) { mDistance = (int) (mLastDownY - event.getY()); if ((mDistance < 0 && getFirstVisiblePosition() == 0 && getChildAt(0).getTop() == 0) || (mDistance > 0 && getLastVisiblePosition() == getCount() - 1)) { mDistance /= 2; scrollTo(0, mDistance); return true; } } mDistance = 0; break; } return super.onTouchEvent(event); } public void run() { mDistance += mDistance > 0 ? -mStep : mStep; scrollTo(0, mDistance); if ((mPositive && mDistance <= 0) || (!mPositive && mDistance >= 0)) { scrollTo(0, 0); mDistance = 0; mLastDownY = 0f; return; } mStep += 1; this.postDelayed(this, 10); } }
2.阻尼效果 scrollview
public class CustomScrollView extends ScrollView { private View inner;// 孩子View private float y;// 点击时y坐标 private Rect normal = new Rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.) private boolean isCount = false;// 是否开始计算 private boolean isMoveing = false;// 是否开始移动. private ImageView imageView; private int initTop, initbottom;// 初始高度 private int top, bottom;// 拖动时时高度。 public void setImageView(ImageView imageView) { this.imageView = imageView; } public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); } /*** * 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate * 方法,也应该调用父类的方法,使该方法得以执行. */ @Override protected void onFinishInflate() { if (getChildCount() > 0) { inner = getChildAt(0); } } /** touch 事件处理 **/ @Override public boolean onTouchEvent(MotionEvent ev) { if (inner != null) { commOnTouchEvent(ev); } return super.onTouchEvent(ev); } /*** * 触摸事件 * * @param ev */ public void commOnTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: top = initTop = imageView.getTop(); bottom = initbottom = imageView.getBottom(); break; case MotionEvent.ACTION_UP: isMoveing = false; // 手指松开. if (isNeedAnimation()) { animation(); } break; /*** * 排除出第一次移动计算,因为第一次无法得知y坐标, 在MotionEvent.ACTION_DOWN中获取不到, * 因为此时是MyScrollView的touch事件传递到到了LIstView的孩子item上面.所以从第二次计算开始. * 然而我们也要进行初始化,就是第一次移动的时候让滑动距离归0. 之后记录准确了就正常执行. */ case MotionEvent.ACTION_MOVE: final float preY = y;// 按下时的y坐标 float nowY = ev.getY();// 时时y坐标 int deltaY = (int) (nowY - preY);// 滑动距离 if (!isCount) { deltaY = 0; // 在这里要归0. } if (deltaY < 0 && top <= initTop) return; // 当滚动到最上或者最下时就不会再滚动,这时移动布局 isNeedMove(); if (isMoveing) { // 初始化头部矩形 if (normal.isEmpty()) { // 保存正常的布局位置 normal.set(inner.getLeft(), inner.getTop(), inner.getRight(), inner.getBottom()); } // 移动布局 inner.layout(inner.getLeft(), inner.getTop() + deltaY / 3, inner.getRight(), inner.getBottom() + deltaY / 3); top += (deltaY / 6); bottom += (deltaY / 6); imageView.layout(imageView.getLeft(), top, imageView.getRight(), bottom); } isCount = true; y = nowY; break; default: break; } } /*** * 回缩动画 */ public void animation() { TranslateAnimation taa = new TranslateAnimation(0, 0, top + 200, initTop + 200); taa.setDuration(200); imageView.startAnimation(taa); imageView.layout(imageView.getLeft(), initTop, imageView.getRight(), initbottom); // 开启移动动画 TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(), normal.top); ta.setDuration(200); inner.startAnimation(ta); // 设置回到正常的布局位置 inner.layout(normal.left, normal.top, normal.right, normal.bottom); normal.setEmpty(); isCount = false; y = 0;// 手指松开要归0. } // 是否需要开启动画 public boolean isNeedAnimation() { return !normal.isEmpty(); } /*** * 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度 * * getHeight():获取的是屏幕的高度 * * @return */ public void isNeedMove() { int offset = inner.getMeasuredHeight() - getHeight(); int scrollY = getScrollY(); // Log.e("jj", "scrolly=" + scrollY); // 0是顶部,后面那个是底部 if (scrollY == 0 || scrollY == offset) { isMoveing = true; } } }
public class RoundedImageView extends ImageView { public RoundedImageView(Context context) { super(context); // TODO Auto-generated constructor stub } public RoundedImageView(Context context, AttributeSet attrs) { super(context, attrs); } public RoundedImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onDraw(Canvas canvas) { Drawable drawable = getDrawable(); if (drawable == null) { return; } if (getWidth() == 0 || getHeight() == 0) { return; } Bitmap b = ((BitmapDrawable) drawable).getBitmap(); Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true); int w = getWidth(), h = getHeight(); Bitmap roundBitmap = getCroppedBitmap(bitmap, w); canvas.drawBitmap(roundBitmap, 0, 0, null); } public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) { Bitmap sbmp; if (bmp.getWidth() != radius || bmp.getHeight() != radius) sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false); else sbmp = bmp; Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xffa19774; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight()); paint.setAntiAlias(true); paint.setFilterBitmap(true); paint.setDither(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(Color.parseColor("#BAB399")); canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f, sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(sbmp, rect, rect, paint); return output; } }比如 设置 imageview的 w,h
<com.example.view.RoundedImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:adjustViewBounds="true" android:maxHeight="80dip" android:maxWidth="80dip" android:src="@drawable/aa" />
3.开关按钮(为了兼容版本)
public class SlidButton extends View implements OnTouchListener { private boolean nowChoose = false;// 记录当前按钮是否打开,true为打开,false为关闭 private boolean onSlip = false;// 记录用户是否在滑动 private float downX, nowX; // 按下时的x,当前的x private Rect btn_on, btn_off;// 打开和关闭状态下,游标的Rect private boolean isChgLsnOn = false;//是否设置监听 private OnChangedListener changedLis; int begin,end;//控件当前的位置 private Bitmap bg_on, bg_off, slip_btn; public SlidButton(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SlidButton(Context context) { super(context); init(); } private void init() { // 载入图片资源 bg_on = BitmapFactory.decodeResource(getResources(), R.drawable.sild_bg_on1); bg_off = BitmapFactory.decodeResource(getResources(), R.drawable.sild_bg_off1); slip_btn = BitmapFactory.decodeResource(getResources(), R.drawable.sild_bg_btn1); // 获得需要的Rect数据 btn_on = new Rect(0, 0, slip_btn.getWidth() , slip_btn.getHeight()); btn_off = new Rect(bg_off.getWidth() - slip_btn.getWidth(), 0, bg_off.getWidth(), slip_btn.getHeight()); setOnTouchListener(this); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Matrix matrix = new Matrix(); begin =this.getWidth() - bg_off.getWidth(); end =9; matrix.postTranslate(begin, end); Paint paint = new Paint(); float x; { // if (nowX<(bg_on.getWidth()/2)) //滑动到前半段与后半段的背景不同,在此做判断 if(!nowChoose){ canvas.drawBitmap(bg_off, matrix, paint);//画出关闭时的背景 }else{ canvas.drawBitmap(bg_on, matrix, paint);//画出打开时的背景 } if (onSlip) {//是否是在滑动状态, if(nowX >= bg_on.getWidth()){ //是否划出指定范围,不能让游标跑到外头,必须做这个判断 x = bg_on.getWidth() - slip_btn.getWidth()/2;//减去游标1/2的长度 }else{ x = nowX - slip_btn.getWidth() / 2; } }else { if(nowChoose)//根据现在的开关状态设置画游标的位置 x = btn_off.left; else x = btn_on.left; } if (x < 0 ) //对游标位置进行异常判断.. x = 0; else if(x > bg_on.getWidth() - slip_btn.getWidth()) x = bg_on.getWidth() - slip_btn.getWidth(); canvas.drawBitmap(slip_btn, x + begin, end, paint);//画出游标. } } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) {//根据动作来执行代码 case MotionEvent.ACTION_MOVE: nowX = event.getX(); break; case MotionEvent.ACTION_DOWN: if (event.getX() < begin) return false; onSlip = true; downX = event.getX(); nowX = downX; break; case MotionEvent.ACTION_UP://松开 onSlip = false; boolean lastChoose = nowChoose; /*if (event.getX() >= begin+(bg_on.getWidth()/2)) nowChoose = true; else nowChoose = false; */ nowChoose =!nowChoose; if(isChgLsnOn && (lastChoose != nowChoose))//如果设置了监听器,就调用其方法. changedLis.OnChanged(nowChoose); break; default: break; } invalidate(); return true; } public void SetOnChangedListener(OnChangedListener l){//设置监听器,当状态修改的时候 isChgLsnOn = true; changedLis = l; } public interface OnChangedListener { abstract void OnChanged(boolean checkState); } }
<com.view.SlidButton android:layout_width="50dip" android:layout_height="30dp" />