您的位置:首页 > 其它

自定义ScollView,根据ScollView滚动,改变头部搜索框,以及实现缓解用户焦虑整体布局移动

2017-05-04 15:05 405 查看
最近刚辞职,找工作也是焦头烂额啊。大家都说,android的职位最近不好找。经过我这几个星期的体验,主要是面试的机会都没有,市场供过于求!所以,投了简历,然后在家等面试通知,就做了一个自定义ScollView,我发现市场上很多项目的标题栏,都是根据ScollView的滚动,然后动态改变标题栏。例如:拉钩,淘宝,华为应用        
 先来看个效果,当滑到顶部,滑过在ViewPage上的搜索框是,顶部的绿色搜索框就会隐藏。如果屏幕往上滚,滚过了ViewPage上的搜索框,顶部的绿色搜索框就会出来。另外,还增加了可以整一个布局根据手势触摸拖拽

头部搜索框实现的原理

1.先拿到ViewPage上的搜索框的left,top,right,bottom这几个的位置数值
//用于保存原始的搜索框布局的位置
private Rect normalSearch = new Rect();

if(normalSearch.isEmpty()){
//获取到对应控件的位置相应的数值
normalSearch.set(innerView.getLeft(), innerView.getTop(),
innerView.getRight(), innerView.getBottom());
}

2.获取到搜索框的位置值后,就可以去监听scollview的滚动距离了
/**
* 滚动监听
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
// TODO Auto-generated method stub
super.onScrollChanged(l, t, oldl, oldt);
//对外开放接口,实现监听滚动监听
onScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
Log.e("Ruan", "距离顶部的距离:"+t);
Log.e("Ruan", "测量的传进来的View高度:"+normalSearch.top);
if(t-normalSearch.top<0||t==normalSearch.top){//如果滑到顶部就隐藏edt
onScrollChangeListener.onScrollTopListener();//监听滚动到相应的位置
//         	edt.setVisibility(View.GONE);
innerView.setVisibility(View.VISIBLE);
}else if(t-normalSearch.top>0){
onScrollChangeListener.onScrollBottomListener();//监听滚动到相应的位置
//        	 edt.setVisibility(View.VISIBLE);
innerView.setVisibility(View.GONE);
}
}

3.然后再去写一个滚动监听的接口,给外部提供调用,然就可以根据滚动的距离,实现对头部的相应操作
/**
*
* 对滚动进行监听,提供给外部调用
* @author ruan
*
*/
public interface OnScrollChangeListener {
void onScrollChange(MyScrollview view, int x, int y, int oldx, int oldy);

void onScrollBottomListener();

void onScrollTopListener();
}


4000
整个布局拖拽的原理

1.第一步需要先实现OnTouchEvent触摸事件
/**
* 复写ontouchEvent触摸事件,这个是主要的
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
//从外部传进来一个id,获取你要定位的控件
innerView = view.findViewById(RID);
if (innerView == null) {
//如果获取不到控件,就直接返回父类的ontouch
return super.onTouchEvent(ev);
} else {
if(normalSearch.isEmpty()){
//获取到对应控件的位置相应的数值
normalSearch.set(innerView.getLeft(), innerView.getTop(),
innerView.getRight(), innerView.getBottom());
}
//具体消费的触摸事件操作
commonTouchEvent(ev);
}
return super.onTouchEvent(ev);
}

2.再在相应的action动作中对布局实现控制
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
//按下时,获取到y值
y = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
//先获取按下时的坐标
float preY = y == 0 ? ev.getY() : y;
//获取滑动的距离
float nowY = ev.getY();
//两个相减就得到,滑动的距离
int detailY = (int) (preY - nowY);
//把当前的坐标,赋值给原始的坐标y
y = nowY;
//操作view进行拖动detailY的一半
if (isNeedMove()) {//根据当前的布局view与scollview滚动距离,判断是否需要改变布局view的距离
//布局改变位置之前,记录一下正常状态的位置
if (normal.isEmpty()) {
normal.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
}
view.layout(view.getLeft(), view.getTop() - detailY / 2, view.getRight(), view.getBottom() - detailY / 2);
}
break;
case MotionEvent.ACTION_UP:
y = 0;
//布局回滚到原来的位置
if (isNeedAnimation()) {
animation();
}
break;
}

3.实现布局回滚的动画
/**
* 执行回滚动画
*/
private void animation() {
TranslateAnimation ta = new TranslateAnimation(0, 0, 0, normal.top - view.getTop());
ta.setDuration(200);
//进行动画监听
ta.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//标记动画进行时,不重复执行动画
animationFinish = false;
}

@Override
public void onAnimationEnd(Animation animation) {
//动画结束,清除动画,view布局回到原来的位置
view.clearAnimation();
view.layout(normal.left, normal.top, normal.right, normal.bottom);
normal.setEmpty();
animationFinish = true;
}

@Override
public void onAnimationRepeat(Animation animation) {

}
});
view.startAnimation(ta);
}

4.在这里为止就可以实现整个布局拖拽回滚,完整的代码如下
package com.ruan.sample.view;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.EditText;
import android.widget.ScrollView;

/**
* Created by Administrator on 2017/05/04.
* 自定义scollview实现根据scollview滚动,改变头部显示方式
*/
public class MyScrollview extends ScrollView {

//要操作的整个ScollView布局
private View view;
//要在操作的搜素框的View
private View innerView;

private float y;
//用于保存原始ScollView布局的位置
private Rect normal = new Rect();
//用于保存原始的搜索框布局的位置
private Rect normalSearch = new Rect();
private boolean animationFinish = true;
//scollview滚动监听
private OnScrollChangeListener onScrollChangeListener;

private EditText edt;

private int RID;

public MyScrollview(Context context) {
super(context, null);
}

public void setOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener){
this.onScrollChangeListener = onScrollChangeListener;
}

public MyScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

public void getRID(int RID){
this.RID = RID;
}

public void getEdt(EditText edt){
this.edt = edt;
}
/**
* 滚动监听
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
// TODO Auto-generated method stub
super.onScrollChanged(l, t, oldl, oldt);
//对外开放接口,实现监听滚动监听
onScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
Log.e("Ruan", "距离顶部的距离:"+t);
Log.e("Ruan", "测量的传进来的View高度:"+normalSearch.top);
if(t-normalSearch.top<0||t==normalSearch.top){//如果滑到顶部就隐藏edt
onScrollChangeListener.onScrollTopListener();//监听滚动到相应的位置
// edt.setVisibility(View.GONE);
innerView.setVisibility(View.VISIBLE);
}else if(t-normalSearch.top>0){
onScrollChangeListener.onScrollBottomListener();//监听滚动到相应的位置
// edt.setVisibility(View.VISIBLE);
innerView.setVisibility(View.GONE);
}
}

@Override
protected void onFinishInflate() {
int childCount = getChildCount();
if (childCount > 0) {
// innerView = getChildAt(0);
//获取最外层的布局
view = getChildAt(0);
}
}

/**
* 复写ontouchEvent触摸事件,这个是主要的
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
//从外部传进来一个id,获取你要定位的控件
innerView = view.findViewById(RID);
if (innerView == null) {
//如果获取不到控件,就直接返回父类的ontouch
return super.onTouchEvent(ev);
} else {
if(normalSearch.isEmpty()){
//获取到对应控件的位置相应的数值
normalSearch.set(innerView.getLeft(), innerView.getTop(),
innerView.getRight(), innerView.getBottom());
}
//具体消费的触摸事件操作
commonTouchEvent(ev);
}
return super.onTouchEvent(ev);
}

/**
* 自定义touch事件处理
*
* @param ev
*/
private void commonTouchEvent(MotionEvent ev) {
if (animationFinish) {
//上一次动画完成后才继续执行新的动画(防止冲突)
int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //按下时,获取到y值 y = ev.getY(); break; case MotionEvent.ACTION_MOVE: //先获取按下时的坐标 float preY = y == 0 ? ev.getY() : y; //获取滑动的距离 float nowY = ev.getY(); //两个相减就得到,滑动的距离 int detailY = (int) (preY - nowY); //把当前的坐标,赋值给原始的坐标y y = nowY; //操作view进行拖动detailY的一半 if (isNeedMove()) {//根据当前的布局view与scollview滚动距离,判断是否需要改变布局view的距离 //布局改变位置之前,记录一下正常状态的位置 if (normal.isEmpty()) { normal.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); } view.layout(view.getLeft(), view.getTop() - detailY / 2, view.getRight(), view.getBottom() - detailY / 2); } break; case MotionEvent.ACTION_UP: y = 0; //布局回滚到原来的位置 if (isNeedAnimation()) { animation(); } break; }
}
}
/**
* 执行回滚动画
*/
private void animation() {
TranslateAnimation ta = new TranslateAnimation(0, 0, 0, normal.top - view.getTop());
ta.setDuration(200);
//进行动画监听
ta.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//标记动画进行时,不重复执行动画
animationFinish = false;
}

@Override
public void onAnimationEnd(Animation animation) {
//动画结束,清除动画,view布局回到原来的位置
view.clearAnimation();
view.layout(normal.left, normal.top, normal.right, normal.bottom);
normal.setEmpty();
animationFinish = true;
}

@Override
public void onAnimationRepeat(Animation animation) {

}
});
view.startAnimation(ta);
}

/**
* 判断是否需要回滚
*
* @return
*/
private boolean isNeedAnimation() {
return !normal.isEmpty();
}

/**
* 判断是否需要移动
*
* @return
*/
private boolean isNeedMove() {
int offset = view.getMeasuredHeight() - getHeight();
float y = innerView.getY();
int scrollY = getScrollY();
Log.i("zoubo", "getMeasuredHeight:" + innerView.getMeasuredHeight() + "----getHeight:" + getHeight());
Log.i("zoubo", "offset:" + getHeight() + "----scrollY:" + scrollY);
if (scrollY == 0 || scrollY == offset) {
return true;
}
return false;
}

/**
*
* 对滚动进行监听,提供给外部调用
* @author ruan
*
*/
public interface OnScrollChangeListener {
void onScrollChange(MyScrollview view, int x, int y, int oldx, int oldy);

void onScrollBottomListener();

void onScrollTopListener();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐