首先要知道 自定义scrollview 仿QQ效果 下拉伸缩放大顶部图片 的原理是监听ontouch事件,在MotionEvent.ACTION_MOVE事件时候,使用不同倍数的系数,重置布局位置【注此处是伸缩隐藏,不是同比例放大】
inner.layout(normal.left, (int) (normal.top + inner_move_H),
normal.right, (int) (normal.bottom + inner_move_H));
关于“自定义scrollview 仿QQ效果 下拉放大顶部图片,上拉回弹”这个问题,网上有很多demo,可是或多或少都有一些问题。比如,上拉滚动不回弹,事件分发处理等。这里主要解决调的问题如下:
1. ScrollView+RelativeLayout 高度计算不准确 当设置margin为负数的时候显示无效果。此时的解决办法是使用FrameLayout、LinearLayout代替完成布局
参考文档http://blog.csdn.net/peidonghui/article/details/8502190
android总结之ScrollView与RelativeLayout和LinearLayout同时使用时问题总结
2.事件分发处理和滚动位置的处理,对处理子控件如button和父控件获取焦点,点击冲突,产生跳跃的问题时候,可以在
case MotionEvent.ACTION_MOVE:
requestDisallowInterceptTouchEvent(true);
设置滚动时子控件在滚动时不获取焦点
http://thoreau.iteye.com/blog/2002272
3.标题变化
标题透明度的变化主要是设置监听,监听滚动的位置,并根据位置计算标题透明度
控件修改自http://blog.csdn.net/jj120522/article/details/8938308?utm_source=tuicool&utm_medium=referral
下面放出控件源码
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.ScrollView; public class PersonalScrollView extends ScrollView { private final String TAG = PersonalScrollView.class.getSimpleName(); private View inner;// 孩子View private float touchY;// 点击时Y坐标 private float deltaY;// Y轴滑动的距离 private float initTouchY;// 首次点击的Y坐标 private boolean shutTouch = false;// 是否关闭ScrollView的滑动. private Rect normal = new Rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.) private boolean isMoveing = false;// 是否开始向下移动. private ImageView imageView;// 背景图控件.
// private View line_up;// 上线
// private int line_up_top;// 上线的top
// private int line_up_bottom;// 上线的bottom private int initTop, initBottom;// 初始高度 private int current_Top, current_Bottom;// 拖动时时高度。 //private int lineUp_current_Top, lineUp_current_Bottom;// 上线 private onTurnListener turnListener; // 状态:上部,下部,默认
private enum State {
UP, DOWN, NOMAL
};
private boolean isclick = true; // 默认状态
private State state = State.NOMAL; private ScrollViewListener scrollViewListener = null; public interface ScrollViewListener { void onScrollChanged(PersonalScrollView scrollView, int x, int y,
int oldx, int oldy); } public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
} @Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
} public void setTurnListener(onTurnListener turnListener) {
this.turnListener = turnListener;
} // public void setLine_up(View line_up) {
// this.line_up = line_up;
// } // 注入背景图
public void setImageView(ImageView imageView) {
this.imageView = imageView;
} /***
* 构造方法
*
* @param context
* @param attrs
*/
public PersonalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
} /***
* 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate
* 方法,也应该调用父类的方法,使该方法得以执行.
*/
// @Override
// protected void onFinishInflate() {
//// if (getChildCount() > 0) {
//// inner = getChildAt(0);
//// }
// } public void setinner(View inner) {
this.inner = inner;
} /** touch 事件处理 **/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (inner != null) {
commOnTouchEvent(ev);
}
//super.onTouchEvent(ev);
// ture:禁止控件本身的滑动.
// if (shutTouch)
// return true;
// else{
// return super.onTouchEvent(ev);
// }
return super.onTouchEvent(ev); } @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
//TRUE 当前的控件的onTouchEvent()
//false 子控件的onInterceptTouchEvent()
// if(ev.getAction()==MotionEvent.ACTION_DOWN){
// return super.onInterceptTouchEvent(ev);
// }
// if(ev.getAction()==MotionEvent.ACTION_MOVE){
// return true;
// }
//
// Log.d("isclick", shutTouch + " "+state+" ");
// if (shutTouch)
// return true;
// if(state!=State.NOMAL){
// return true;
// }else if(isclick){
// return true;
// }else {
// return super.onInterceptTouchEvent(ev);
// } // if (!isclick) {
// //向上向下滚动时候不触发子控件的touch
// //屏蔽抬起事件以后,要重置isclick事件,准备下一次点击
// if (!isclick&&State.NOMAL!=state) {
// //一次滚动结束
// isclick = true;
// }
// return true;
// } else
// return super.onInterceptTouchEvent(ev);
// return super.onInterceptTouchEvent(ev);
}
/***
* 触摸事件
*
* @param ev
*/
public void commOnTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
initTouchY = ev.getY();
requestDisallowInterceptTouchEvent(false);
current_Top = initTop = imageView.getTop();
current_Bottom = initBottom = imageView.getBottom();
isclick = true;
break;
case MotionEvent.ACTION_UP:
/** 回缩动画 **/
requestDisallowInterceptTouchEvent(false);
if (isNeedAnimation()) {
animation();
} // if (getScrollY() == 0) {
// state = State.NOMAL;
// }
if(state!=State.NOMAL){
isclick = false;
}else{
isclick = true;
}
state = State.NOMAL; isMoveing = false;
touchY = 0;
shutTouch = false; break; /***
* 排除出第一次移动计算,因为第一次无法得知deltaY的高度, 然而我们也要进行初始化,就是第一次移动的时候让滑动距离归0.
* 之后记录准确了就正常执行.
*/
case MotionEvent.ACTION_MOVE:
requestDisallowInterceptTouchEvent(true);
touchY = ev.getY();
deltaY = touchY - initTouchY;// 滑动距离
isclick = false;
/** 对于首次Touch操作要判断方位:UP OR DOWN **/
if (deltaY < 0 && state == state.NOMAL&&Math.abs(deltaY)>40) { state = State.UP;
} else if (deltaY > 0 && state == state.NOMAL&&Math.abs(deltaY)>40) { state = State.DOWN;
} if (state == State.UP) {
deltaY = deltaY < 0 ? deltaY : 0;
isMoveing = false;
shutTouch = false; } else if (state == state.DOWN) {
if (getScrollY() <= deltaY) {
shutTouch = true;
isMoveing = true;
}
deltaY = deltaY < 0 ? 0 : deltaY;
} if (isMoveing) {
// 初始化头部矩形
if (normal.isEmpty()) {
// 保存正常的布局位置
normal.set(inner.getLeft(), inner.getTop(),
inner.getRight(), inner.getBottom());
}
// 移动布局(手势移动的1/3)
float inner_move_H = deltaY / 5; inner.layout(normal.left, (int) (normal.top + inner_move_H),
normal.right, (int) (normal.bottom + inner_move_H)); /** image_bg **/
float image_move_H = deltaY / 10;
current_Top = (int) (initTop + image_move_H);
current_Bottom = (int) (initBottom + image_move_H);
imageView.layout(imageView.getLeft(), current_Top,
imageView.getRight(), current_Bottom); }
break; default:
break; }
} /***
* 回缩动画
*/
public void animation() { TranslateAnimation image_Anim = new TranslateAnimation(0, 0,
Math.abs(initTop - current_Top), 0);
image_Anim.setDuration(200);
imageView.startAnimation(image_Anim); imageView.layout(imageView.getLeft(), (int) initTop,
imageView.getRight(), (int) initBottom); // 开启移动动画
TranslateAnimation inner_Anim = new TranslateAnimation(0, 0,
inner.getTop(), normal.top);
inner_Anim.setDuration(200);
inner.startAnimation(inner_Anim);
inner.layout(normal.left, normal.top, normal.right, normal.bottom);
normal.setEmpty(); /** 动画执行 **/
if (current_Top > initTop + 50 && turnListener != null)
turnListener.onTurn(); } /** 是否需要开启动画 **/
public boolean isNeedAnimation() {
return !normal.isEmpty();
} /***
* 执行翻转
*
* @author jia
*
*/
public interface onTurnListener { /** 必须达到一定程度才执行 **/
void onTurn();
} }
布局 如图
<FrameLayout 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:background="#f4f4f4" > <com.example.scrollviewdemo.PersonalScrollView
android:id="@+id/personalScrollView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" > <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" > <ImageView
android:id="@+id/iv_personal_bg"
android:layout_width="match_parent"
android:layout_height="300dip"
android:layout_marginTop="-50dip"
android:scaleType="centerCrop"
android:src="@drawable/profiles_default_personal_bg" /> <LinearLayout
android:id="@+id/test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" > <LinearLayout
android:id="@+id/mine_main_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingTop="48dip" > <ImageView
android:id="@+id/avatar"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_marginTop="8dip"
android:src="@drawable/default_user_hole" /> <LinearLayout
android:id="@+id/mine_main_loginedly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" > <TextView
android:id="@+id/mine_main_nikename"
android:layout_width="wrap_content"
android:layout_height="40dip"
android:layout_margin="0dip"
android:background="@null"
android:gravity="center"
android:includeFontPadding="false"
android:padding="0dip"
android:text="用户名"
android:textSize="14sp" /> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="40dip"
android:gravity="center_horizontal"
android:orientation="horizontal" > <TextView
android:id="@+id/mine_main_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="3dip"
android:text="151****6663"
android:textSize="12sp" /> <TextView
android:id="@+id/mine_main_role"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dip"
android:drawablePadding="3dip"
android:text="text特甜"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:paddingBottom="10dip"
android:paddingLeft="15dip"
android:paddingRight="15dip"
android:paddingTop="10dip" > <LinearLayout
android:layout_width="0dip"
android:layout_height="62dip"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="horizontal" > <TextView
android:id="@+id/mine_to_studyrecord"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:layout_weight="1"
android:background="@null"
android:gravity="left|center_vertical"
android:text="mine_main_tab1"
android:textSize="13sp" />
</LinearLayout> <LinearLayout
android:layout_width="0dip"
android:layout_height="62dip"
android:layout_marginLeft="18dip"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="horizontal" > <TextView
android:id="@+id/mine_to_myorder"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:layout_weight="1"
android:background="@null"
android:gravity="left|center_vertical"
android:text="mine_main_tab2"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="vertical"> <TextView
android:id="@+id/mine_to_mystore"
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" /> <TextView
android:layout_width="match_parent"
android:layout_height="38dip"
android:text="mine_main_mycollection" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</com.example.scrollviewdemo.PersonalScrollView> <RelativeLayout
android:id="@+id/mine_title"
android:layout_width="match_parent"
android:layout_height="48dip" > <TextView
android:id="@+id/common_title_left_text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="取消"
android:textColor="#FFFFFF" /> <TextView
android:id="@+id/common_title_middle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textSize="20sp" /> </RelativeLayout> </FrameLayout>
上面是布局的简单示意