Android自定义控件——有弹性的ListView,ScrollView

上一次我们试验了有弹性的ScrollView。详情

这一次,我们来试验有弹性的ScrollView。

国际惯例,效果图:



主要代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. import android.content.Context;  
  2. import android.graphics.Rect;  
  3. import android.util.AttributeSet;  
  4. import android.view.MotionEvent;  
  5. import android.view.animation.Animation;  
  6. import android.view.animation.Animation.AnimationListener;  
  7. import android.view.animation.TranslateAnimation;  
  8. import android.widget.AbsListView;  
  9. import android.widget.ListView;  
  10.   
  11. /** 
  12.  * ElasticScrollView有弹性的ListView 
  13.  */  
  14. public class ElasticListView extends ListView {  
  15.     private float y;  
  16.     private Rect normal = new Rect();  
  17.     private boolean animationFinish = true;  
  18.   
  19.     public ElasticListView(Context context) {  
  20.         super(context);  
  21.         init();  
  22.     }  
  23.   
  24.     public ElasticListView(Context context, AttributeSet attrs) {  
  25.         super(context, attrs);  
  26.         init();  
  27.     }  
  28.   
  29.     protected void onScrollChanged(int l, int t, int oldl, int oldt) {  
  30.   
  31.     }  
  32.   
  33.     boolean overScrolled = false;  
  34.     private void init() {  
  35.         setOnScrollListener(new OnScrollListener() {  
  36.             @Override  
  37.             public void onScrollStateChanged(AbsListView view, int scrollState) {  
  38.             }  
  39.   
  40.             @Override  
  41.             public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
  42.                 overScrolled = false;  
  43.             }  
  44.         });  
  45.     }  
  46.       
  47.     @Override  
  48.     protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {  
  49.         overScrolled = true;  
  50.     }  
  51.   
  52.     @Override  
  53.     public boolean onTouchEvent(MotionEvent ev) {  
  54.         commOnTouchEvent(ev);  
  55.         return super.onTouchEvent(ev);  
  56.     }  
  57.   
  58.     public void commOnTouchEvent(MotionEvent ev) {  
  59.         if (animationFinish) {  
  60.             int action = ev.getAction();  
  61.             switch (action) {  
  62.             case MotionEvent.ACTION_DOWN:  
  63.                 y = ev.getY();  
  64.                 break;  
  65.             case MotionEvent.ACTION_UP:  
  66.                 y = 0;  
  67.                 if (isNeedAnimation()) {  
  68.                     animation();  
  69.                 }  
  70.                 break;  
  71.             case MotionEvent.ACTION_MOVE:  
  72.                 final float preY = y == 0 ? ev.getY() : y;  
  73.                 float nowY = ev.getY();  
  74.                 int deltaY = (int) (preY - nowY);  
  75.   
  76.                 y = nowY;  
  77.                 // 当滚动到最上或者最下时就不会再滚动,这时移动布局  
  78.                 if (isNeedMove(deltaY)) {  
  79.                     if (normal.isEmpty()) {  
  80.                         // 保存正常的布局位置  
  81.                         normal.set(getLeft(), getTop(), getRight(), getBottom());  
  82.                     }  
  83.                     // 移动布局  
  84.                     layout(getLeft(), getTop() - deltaY / 2, getRight(), getBottom() - deltaY / 2);  
  85.                 }  
  86.                 break;  
  87.             default:  
  88.                 break;  
  89.             }  
  90.         }  
  91.     }  
  92.   
  93.     // 开启动画移动  
  94.     public void animation() {  
  95.         // 开启移动动画  
  96.         TranslateAnimation ta = new TranslateAnimation(000, normal.top - getTop());  
  97.         ta.setDuration(200);  
  98.         ta.setAnimationListener(new AnimationListener() {  
  99.             @Override  
  100.             public void onAnimationStart(Animation animation) {  
  101.                 animationFinish = false;  
  102.   
  103.             }  
  104.   
  105.             @Override  
  106.             public void onAnimationRepeat(Animation animation) {  
  107.   
  108.             }  
  109.   
  110.             @Override  
  111.             public void onAnimationEnd(Animation animation) {  
  112.                 clearAnimation();  
  113.                 // 设置回到正常的布局位置  
  114.                 layout(normal.left, normal.top, normal.right, normal.bottom);  
  115.                 normal.setEmpty();  
  116.                 animationFinish = true;  
  117.             }  
  118.         });  
  119.         startAnimation(ta);  
  120.     }  
  121.   
  122.     // 是否需要开启动画  
  123.     public boolean isNeedAnimation() {  
  124.         return !normal.isEmpty();  
  125.     }  
  126.   
  127.     // 是否需要移动布局  
  128.     public boolean isNeedMove(float deltaY) {  
  129.         if (overScrolled && getChildCount() > 0) {  
  130.             if (getLastVisiblePosition() == getCount() - 1 && deltaY > 0) {  
  131.                 return true;  
  132.             }  
  133.             if (getFirstVisiblePosition() == 0 && deltaY < 0) {  
  134.                 return true;  
  135.             }  
  136.         }  
  137.         return false;  
  138.     }  
  139. }  

测试代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.     ElasticListView listView;  
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.           
  8.         listView = (ElasticListView) findViewById(R.id.listview);  
  9.           
  10.         String[] listValues = new String[20];  
  11.         for (int i=0;i<listValues.length;i++) {  
  12.             listValues[i] = "TextView" + i;  
  13.         }  
  14.         listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listValues));  
  15.     }  
  16. }  

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.     ElasticListView listView;  
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.           
  8.         listView = (ElasticListView) findViewById(R.id.listview);  
  9.           
  10.         String[] listValues = new String[20];  
  11.         for (int i=0;i<listValues.length;i++) {  
  12.             listValues[i] = "TextView" + i;  
  13.         }  
  14.         listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listValues));  
  15.     }  
  16. }  
public class FlexibleScrollView extends ScrollView {  
  
    private Context mContext;  
    private static int mMaxOverDistance = 50;  
  
    public FlexibleScrollView(Context context, AttributeSet attrs,  
            int defStyleAttr) {  
        super(context, attrs, defStyleAttr);  
        this.mContext = context;  
        initView();  
    }  
  
    public FlexibleScrollView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        this.mContext = context;  
        initView();  
    }  
  
    public FlexibleScrollView(Context context) {  
        super(context);  
        this.mContext = context;  
        initView();  
    }  
  
    private void initView() {  
        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();  
        float density = metrics.density;  
        mMaxOverDistance = (int) (density * mMaxOverDistance);  
    }  
  
    @Override  
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,  
            int scrollY, int scrollRangeX, int scrollRangeY,  
            int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {  
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,  
                scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverDistance,  
                isTouchEvent);  
    }  
}  
通过上面这个类也可以实现弹性效果

二、仿朋友圈背景图片下拉

public class ScrollDampView extends ScrollView {
	/** 该属性具体参数 怎么控制 未解!!!! */
	private static final int LEN = 0xc8;
	/** 回弹时所用的时间 */
	private static final int DURATION = 200;
	/** 最大Y坐标 其值一般设定为Scroller对应控件的高度 */
	private static final int MAX_DY = 200;

	private Scroller mScroller;
	/** 阻尼系数 */
	private static final float OFFSET_RADIO = 2.5f;

	private float startY;
	private int imageViewH;

	private ImageView imageView;
	private boolean scrollerType;

	public ScrollDampView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

	}

	public ScrollDampView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mScroller = new Scroller(context);
	}

	public ScrollDampView(Context context) {
		super(context);

	}

	public void setImageView(ImageView imageView) {
		this.imageView = imageView;
	}

	float curY;

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		int action = event.getAction();
		if (!mScroller.isFinished()) {
			return super.onTouchEvent(event);
		}
		switch (action) {
		case MotionEvent.ACTION_DOWN:// 变量赋初始值
			imageViewH = imageView.getHeight();
			startY = event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			if (imageView.isShown()) {
				float deltaY = (event.getY() - startY ) / OFFSET_RADIO;
				Log.i("syso", "deltaY: "+deltaY+" imageTop: "+imageView.getTop());
				//往下拉
				if (deltaY > 0 && deltaY <= imageView.getBottom() + LEN) {
					android.view.ViewGroup.LayoutParams params = imageView.getLayoutParams();
					params.height = (int) (imageViewH + deltaY);// 改变高度
					imageView.setLayoutParams(params);
				}
				scrollerType = false;
			}
			break;
		case MotionEvent.ACTION_UP:
			scrollerType = true;
			// 开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位
			// ,即到达坐标为(startX+dx , startY+dy)处
			mScroller.startScroll(imageView.getLeft(), imageView.getBottom(),
					0 - imageView.getLeft(),
					imageViewH - imageView.getBottom(), DURATION);
			invalidate();
			break;
		}

		return super.dispatchTouchEvent(event);
	}

	// //该mScroller针对于imageView的变化
	// 被父视图调用,用于必要时候对其子视图的值(mScrollX和mScrollY)
	// 进行更新。典型的情况如:父视图中某个子视图使用一个Scroller对象来实现滚动操作,会使得此方法被调用。
	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			int x = mScroller.getCurrX();
			int y = mScroller.getCurrY();// ImageView的当前Y坐标
			imageView.layout(0, 0, x + imageView.getWidth(), y);// 使imageView本身做相应变化
			invalidate();
			// 滑动还未完成时,手指抬起时,当前y坐标大于其实imageView的高度时
			// 设定imageView的布局参数 作用:使除imageView之外的控件做相应变化
			if (!mScroller.isFinished() && scrollerType && y > MAX_DY) {
				android.view.ViewGroup.LayoutParams params = imageView
						.getLayoutParams();
				params.height = y;
				imageView.setLayoutParams(params);
			}
		}
	}

}

布局文件:

<com.example.dampview.ScrollDampView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dampview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >

        <!--此处必须设置imageview的scaleType为centerCrop,当然在代码中设置也可以-->
        <ImageView
            android:id="@+id/img"
            android:layout_width="match_parent"
            android:layout_height="160dp"
            android:scaleType="centerCrop"
            android:src="@drawable/image" />

        <ImageView
            android:id="@+id/iv_photo"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_marginTop="-32dp"
            android:src="@drawable/ic_launcher" 
            />

    </LinearLayout>

</com.example.dampview.ScrollDampView>


posted @ 2015-03-13 19:45  若 ♂ 只如初见  阅读(1133)  评论(0编辑  收藏  举报