項目中用到了商品詳情展示效果,所以立馬想到借鑒天貓商品詳情界面,看了天貓的詳情頁面想到了兩套解決方案。1,使用LitView ?添加header監聽listView 的滑動然后根據listView 的滑動距離計算 header應該滑動的距離 和改變header的高度。2,使用ScrollView 代替1中的ListView ?監聽onTouch事件,動態改變header的高度,按照這個思路也可以實現ScrollView上下拉的回彈效果或者是上下拉刷新,思路都是一樣。
由于項目的商品詳情返回的數據 并不是一個集合 而且內容不統一所以使用方案2,下面先看看效果圖還是圖片有說服力。
這里的主要思路是:計算手指下拉滑動的距離然后設置給header布局,當手指松開時在把header的高度修改回原來的高度,這里用到了開源的動畫庫nineoldandroids(只需要在build引用compile files('libs/nineoldandroids-2.4.0.jar')),在計算手指下拉滑動的距離時候需要判斷ScrollView到達頂部的條件
上拉時候 判斷ScrollView到達底部后然后的不走和上拉是一樣的
下拉的時候 判斷header的高度答到一個臨界值的時候 打開header布局
下面是ScrollView 的完整代碼
packagecom.app.test.myscrollview;
importandroid.content.Context;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.LinearLayout;
importandroid.widget.ScrollView;
importcom.nineoldandroids.animation.ValueAnimator;
/**
* Created by Administrator on 2015/12/18.
*/
public classMyScrollViewextendsScrollView {
privateViewGroupinnerLayout;//ScrololView里的布局
privateViewheaderView;//頭布局 必須在ScrollView里面
private intoriginalHeight;//頭布局原始高度
private floatdownY;//手指按下的Y坐標
privateViewemputyView;//空的布局? 用于占位符
privateViewfooterView;//底部布局
private booleanisOpen;
private booleanisOpening;
protected final static floatOFFSET_RADIO=1.8f;//偏移量
protected final static floatOPEN_RADIO=1.8f;//打開比例
publicMyScrollView(Context context) {
this(context,null);
}
publicMyScrollView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
publicMyScrollView(Context context, AttributeSet attrs,intdefStyleAttr) {
super(context, attrs, defStyleAttr);
}
//布局已經加載完成后調用? 一些params參數在這里都能取到值了
@Override
protected voidonFinishInflate() {
super.onFinishInflate();
final intchildCount = getChildCount();
if(childCount ==1) {
innerLayout= (ViewGroup) getChildAt(0);
emputyView=newLinearLayout(getContext());
ViewGroup.LayoutParams lp =newViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,0);
emputyView.setLayoutParams(lp);
footerView=newLinearLayout(getContext());
footerView.setLayoutParams(lp);
innerLayout.addView(emputyView,0);
innerLayout.addView(footerView,innerLayout.getChildCount());
}else{
throw newRuntimeException("ScrollView只能有一個子布局");
}
}
public voidsetOpen(booleanopen) {
isOpen= open;
}
public voidsetOpening(booleanopening) {
isOpening= opening;
}
public voidsetHeaderView(View headerView) {
if(headerView !=null) {
this.headerView= headerView;
originalHeight= headerView.getLayoutParams().height;
}
}
public voidsetOpenViewListener(OpenViewListener openViewListener) {
this.openViewListener= openViewListener;
}
OpenViewListeneropenViewListener;
public interfaceOpenViewListener {
public voidopenVeiw(View headerVeiw);
}
@Override
public booleanonTouchEvent(MotionEvent ev) {
final intaction = ev.getAction();
switch(action) {
caseMotionEvent.ACTION_DOWN:
downY= ev.getRawY();
getParent().requestDisallowInterceptTouchEvent(true);
break;
caseMotionEvent.ACTION_MOVE:
floattempY = ev.getRawY();
floatdelatY = tempY -downY;//手指豎直方向滑動的距離
downY= tempY;
floatscrollY = getScrollY();//豎直方向 滾動的值
floatoffset =innerLayout.getMeasuredHeight() - getHeight();//偏移量
if(scrollY ==0&& delatY >0) {//表示滑動到頂部了
intopenOffset = (int) (delatY /OFFSET_RADIO);
if(headerView!=null) {
intafterHeight = upDateViewHeight(headerView, openOffset);
if(afterHeight >originalHeight*OPEN_RADIO&&openViewListener!=null&& !isOpen) {
//TODO需要打開
isOpening=true;
openViewListener.openVeiw(headerView);
}else{
setViewHeight(headerView, afterHeight);
}
}else{
intafterHeight = upDateViewHeight(emputyView, openOffset);
setViewHeight(emputyView, afterHeight);
}
}
if(scrollY == offset && delatY <0) {//滑動到底部了
intafterHeight = upDateViewHeight(footerView, (int) -delatY);
setViewHeight(footerView, afterHeight);
}
break;
caseMotionEvent.ACTION_CANCEL:
caseMotionEvent.ACTION_UP://手指彈開
//手指彈開 讓布局的高度 從現在的高度變成0使用動畫 也可以使用Scroller使用動畫簡單
if(headerView!=null&&headerView.getHeight() >originalHeight&& !isOpening) {
closeView(headerView,headerView.getHeight(),originalHeight);
}else if(emputyView.getHeight() >0) {
closeView(emputyView,emputyView.getHeight(),0);
}
if(footerView.getHeight() >0) {
closeView(footerView,footerView.getHeight(),0);
}
break;
}
return super.onTouchEvent(ev);
}
public voidcloseView(finalView view,intfromHeight,final inttoHeight) {
ValueAnimator animator = ValueAnimator.ofInt(fromHeight, toHeight);
animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener() {
@Override
public voidonAnimationUpdate(ValueAnimator valueAnimator) {
intheight = (int) valueAnimator.getAnimatedValue();
view.getLayoutParams().height= height;
view.setLayoutParams(view.getLayoutParams());
if(view==headerView&& height ==toHeight) {
isOpen=false;
isOpening=false;
}
}
});
animator.start();
animator.setDuration(300);
}
public voidopenView(finalView view,intfromHeight,final inttoHeight) {
ValueAnimator animator = ValueAnimator.ofInt(fromHeight, toHeight);
animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener() {
@Override
public voidonAnimationUpdate(ValueAnimator valueAnimator) {
intheight = (int) valueAnimator.getAnimatedValue();
view.getLayoutParams().height= height;
view.setLayoutParams(view.getLayoutParams());
if(height ==toHeight&&view==headerView) {
isOpen=true;
isOpening=false;
}
}
});
animator.start();
animator.setDuration(300);
}
/**
*改變 布局的高度
*
*@paramview
*@paramupDateHeight更新的高度
*@return改變后的高度
*/
public intupDateViewHeight(View view,intupDateHeight) {
intnowHeight = view.getLayoutParams().height;
intafterHeight = nowHeight + upDateHeight;
returnafterHeight;
}
/**
*設置高度
*
*@paramview
*@paramafterHeight
*/
public voidsetViewHeight(View view,intafterHeight) {
view.getLayoutParams().height= afterHeight;
view.setLayoutParams(view.getLayoutParams());
}
}
下面是布局文件
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="190dp"
>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="@string/text"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/goods_sample"/>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="@string/text"/>
在Activity中引用
packagecom.app.test;
importandroid.support.v4.view.PagerAdapter;
importandroid.support.v7.app.AppCompatActivity;
importandroid.os.Bundle;
importandroid.util.DisplayMetrics;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.BaseAdapter;
importandroid.widget.TextView;
importcom.app.test.demo.GListView;
importcom.app.test.demo.MyViewPager;
importcom.app.test.myscrollview.BaseViewPgerAdapter;
importcom.app.test.myscrollview.MyScrollView;
importjava.util.ArrayList;
importjava.util.List;
public classMainActivityextendsAppCompatActivity {
MyViewPagermyViewPager;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewPager= (MyViewPager) findViewById(R.id.header);
List list =newArrayList<>();
list.add("");
list.add("");
list.add("");
list.add("");
myViewPager.setAdapter(newBaseViewPgerAdapter(list, R.layout.item_img) {
@Override
public voidgetView(View view, String item,intposition) {
}
});
finalMyScrollView myScrollView = (MyScrollView) findViewById(R.id.scrollView);
myScrollView.setHeaderView(myViewPager);
myScrollView.setOpenViewListener(newMyScrollView.OpenViewListener() {
@Override
public voidopenVeiw(View headerVeiw) {
myScrollView.openView(headerVeiw, headerVeiw.getHeight(), getScreenHeight());
}
});
}
/**
*得到屏幕高度
*
*@return高度
*/
public intgetScreenHeight() {
DisplayMetrics dm =newDisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
intscreenHeight = dm.heightPixels;
returnscreenHeight;
}
}
這個里的ViewPager是自定義ViewPager 因為 如果不對ViewPager做處理的話會產生滑動沖突導致ViewPager不能滑動的后果,對ViewPager的處理也比較簡單主要是在dispatchTouchEvent事件里重新分發事件
@Override
public booleandispatchTouchEvent(MotionEvent ev) {
intaction = ev.getAction();
if(action == MotionEvent.ACTION_DOWN) {
downX=tempX= (int) ev.getX();
downY=tempY= (int) ev.getY();
}else if(action == MotionEvent.ACTION_UP) {
//? ? ? ? ? ? currentPage = this.getCurrentItem() + 1;
}else if(action == MotionEvent.ACTION_MOVE) {
intmoveX = (int) ev.getX();
intmoveY = (int) ev.getY();
intdeltaX =tempX- moveX;
intdeltaY =tempY- moveY;
tempX= moveX;
tempY= moveY;
if(Math.abs(deltaY) > Math.abs(deltaX)) {
getParent().requestDisallowInterceptTouchEvent(false);
return super.dispatchTouchEvent(ev);
}
}
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
這是ViewPager的 dispatchTouchEvent方法 就是判斷了手指滑動的水平距離和豎直距離?
如果豎直方向的距離大于水平方向的距離則調用
getParent().requestDisallowInterceptTouchEvent(false);
這個方法的作用就是告訴父布局 可以攔截ViewPager的事件 這是ViewPager的ontouch不起作用
反之 當水平距離大于豎直距離時 則需要
getParent().requestDisallowInterceptTouchEvent(true);
告訴父容器不需要攔截事件 viewPager自己處理事件
在Activity中ViewPager設置的Adapter 是自己封裝了一個PagerAdapter 這樣寫的好處就是省去了大量重復代碼 其代碼是:
packagecom.app.test.myscrollview;
importandroid.support.v4.view.PagerAdapter;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.ImageView;
importjava.util.List;
/**
* Created by Administrator on 2015/12/18.
*/
public abstract classBaseViewPgerAdapterextendsPagerAdapter {
Listdatas;
intlayoutId;
publicBaseViewPgerAdapter(List datas,intlayoutId) {
this.datas= datas;
this.layoutId= layoutId;
}
@Override
public intgetCount() {
returndatas==null?0:datas.size();
}
@Override
public booleanisViewFromObject(View view, Object object) {
returnview == object;
}
@Override
public voiddestroyItem(ViewGroup container,intposition, Object object) {
container.removeView((View) object);
}
public abstract voidgetView(View view,Titem,intposition);
@Override
publicObject instantiateItem(ViewGroup container,intposition) {
View view = LayoutInflater.from(container.getContext()).inflate(layoutId,null);
Titem =datas.get(position);
getView(view, item, position);
container.addView(view);
returnview;
}
publicImageView setImageViewRec(View view,intimgId,intimageRec) {
ImageView img = (ImageView) view.findViewById(imgId);
img.setImageResource(imageRec);
returnimg;
}
}
好了到此結束了。 大致能夠實現天貓商品詳情的界面,當然這里還有需要可以改進的地方
比如在上拉的時候 會有一絲絲的卡頓現象 暫時還沒有找到解決辦法 我想應該是因為手指輕微抖動導致footer的高度不斷變化。
還有就是 簡書怎么復制代碼呀。。 ?這樣復制的代碼一點都不好呀,同時也求一款好的Gif截屏工具