序言
國慶放假過后眼看一年又要過完了,年初指望著已經有一年的經驗本以為自己不是剛出校的學生以為翅膀已經硬了,打算辭職換新工作,一面試才發現自己就是個垃圾,什么oninterceptEvent,dispatchTouchEvent ,Aysnctask都不會。做了一年的項目也是用的Xutils2.6版本 還有一堆不常用不好的不主流不時尚的框架,技術也沒任何長進。還好公司真的輕松(所以也學不到任何東西)可以趁閑下來的時間多學點東西。于是寫了個簡單但也有需求的控件練練手。
首先先看效果圖吧
這個是listview的效果還有一個ScrollView的效果當然使用和實現時一樣的原理這里就一listview來講解,文末傳送門可以看到全部的代碼
1、具體使用
項目build.gradle
allprojects {
repositories {
jcenter()
maven { url 'https://jitpack.io' }
}
}
app model build.gradle
compile 'com.github.xypmhxy:PullZoomLayout:1.1'
布局文件中
<com.ren.pullzoom.widget.PullZoomLayout
android:id="@+id/pull"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:image_height="200dp" 圖片高度
app:image_res="@mipmap/timg" 圖片資源
app:refresh_enable="true" 是否開啟刷新
app:scale_type="center_crop">//圖片縮放方式
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
</com.ren.pullzoom.widget.PullZoomLayout>
2、實現思路
其思路很簡單
1.首先在構造方法中動態添加下拉縮放的imageView和刷新的refreshProgress(控件中為實現跟隨手指滑動旋轉因此使用的為imageView)
2.獲取到listview對象,然后監聽listview的滑動事件,判斷滑到頂部后繼續向下滑動的時候將需要放大的ImageView高度增加然后利用ImageView的Scale方法完成縮放。
3.最后放開手指的時候用屬性動畫讓imageView平滑回到最初狀態,并且如果開啟下拉刷新則回調其方法。
3、具體實現
1.動態添加兩個ImageView(下拉放大的和刷新的progress),大致原理就是將這兩個ImageView添加到RelativeLayout中然后將RelativeLayout 添加到自身中。代碼如下
/*實例化頭部布局包含pullZoomImage 和 refreshProgress*/
protected void init(Context context) {
RelativeLayout head = new RelativeLayout(context);
ViewGroup.LayoutParams headParams = new ViewGroup.LayoutParams(-1, -2);
head.setLayoutParams(headParams);
/*實例化pullZoomImage*/
·······
pullZoomImage.setImageResource(imageRes);
originalParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, imageHeight);
pullZoomImage.setLayoutParams(originalParams);
head.addView(pullZoomImage);
/*實例化refreshProgress*/
refreshProgress = new ImageView(context);
refreshProgress.setVisibility(GONE);
refreshProgress.setImageResource(R.drawable.refresh);
RelativeLayout.LayoutParams refreshParams = new RelativeLayout.LayoutParams(dip2px(context, 35), dip2px(context, 35));
refreshParams.addRule(RelativeLayout.ALIGN_PARENT_END, RelativeLayout.TRUE);
refreshProgress.setLayoutParams(refreshParams);
head.addView(refreshProgress);
/*將頭部添加到此控件中*/
addView(head, 0);
}
2.是獲取listview對象,因為listview屬于子控件所以不能在構造方法里直接獲取,因為此時控件不一定加載完成所以需要等待子控件加載完成后獲取因此在onFinishInflate方法中獲取
@Override
protected void onFinishInflate() {
super.onFinishInflate();
/*獲取listview*/
if (getChildAt(1) instanceof ListView) {
listView = (ListView) getChildAt(1);
listView.setOnScrollListener(scrollListener);
listView.setOnTouchListener(touchListener);
}
}
3.添加listview滑動監聽判斷是否滑動到頂部,可以開啟下拉放大功能
/*listview滑動監聽*/
protected AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
/*判斷是否滑動到頂部*/
int firstVisibleItem = listView.getFirstVisiblePosition();
if (firstVisibleItem == 0 && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
View firstView = getChildAt(0);
canZoom = firstView != null && firstView.getTop() == 0;
} else
canZoom = false;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
};
4.實現OnTouchListener根據事件調用放大和縮小動畫,抬手時實現刷新等操作
/*listview touchListener監聽*/
protected OnTouchListener touchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent ev) {
if (pullZoomImage == null) return false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
pressY = ev.getY();//獲取按下的Y坐標
break;
case MotionEvent.ACTION_MOVE:
if (canZoom)//如果已經滑動到頂部并繼續滑動則開始放大pullZoomImage
return zoomView(ev);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (canZoom)
restroe();//還原pullZoomImage動畫
if (needRefresh && refreshListener != null) {//達到刷新條件并且實現刷新監聽
refreshListener.onRefresh();
rotationProgress();//刷新時progress旋轉動畫
} else
refreshProgress.setVisibility(GONE);
//重置變量
needRefresh = false;
canZoom = false;
break;
}
return false;
}
};
縮放imageview
/*放大pullZoomImage*/
protected boolean zoomView(MotionEvent ev) {
float offY = ev.getY() - pressY;
if (offY <= 0 || offY < 16)//滑動方向上滑或者滑動距離小于16則不管
return false;
/*如果開啟下拉刷新判斷滑動距離是否大于refrshSlop則顯示refreshProgress*/
if (refreshEnable) {
needRefresh = offY >= refrshSlop;
if (needRefresh)
refreshProgress.setVisibility(VISIBLE);
}
ViewGroup.LayoutParams params = pullZoomImage.getLayoutParams();
float height = originalParams.height + offY / damp;//根據滑動距離增加pullZoomImage的高度
params.height = (int) height;
scaleImage(height);//放大圖片
rotationProgress(offY);//旋轉refreshProgress
if (params.height >= originalParams.height)
pullZoomImage.setLayoutParams(params);//為pullZoomImage設置改變后的params
return true;
}
/*縮放imageview*/
protected void scaleImage(float height) {
// if (pullZoomImage.getScaleType() == ImageView.ScaleType.CENTER_CROP)
// return;
float scale = (height - originalParams.height) / originalParams.height;//根據滑動的大小判斷縮放比例
pullZoomImage.setScaleX(1 + scale);
pullZoomImage.setScaleY(1 + scale);
}
抬手后通過屬性動畫還原pullZoomImage
/*放開后還原pullZoomImage*/
protected void restroe() {
ValueAnimator animator = ValueAnimator.ofFloat(pullZoomImage.getLayoutParams().height, originalParams.height);// 動畫更新的監聽
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator arg0) {
float height = (float) arg0.getAnimatedValue();// 獲取動畫當前變化的值
// 根據最新高度,更新布局高度
ViewGroup.LayoutParams params = pullZoomImage.getLayoutParams();
params.height = (int) height;
scaleImage(height);
pullZoomImage.setLayoutParams(params);
}
});
animator.setDuration(200);// 動畫時間
animator.start();// 開啟動畫
}
大致原理就是這樣最后傳送門開啟 PullZoomLayout