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

	最近刚辞职,找工作也是焦头烂额啊。大家都说,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();
    }

整个布局拖拽的原理

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();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在FastAdmin框架中实现自定义搜索框,你可以按照以下步骤进行操作: 1. 打开菜单管理页面。在FastAdmin框架中,通常可以通过`http://yourdomain/admin/menu/index`访问菜单管理页面。 2. 找到你想要添加自定义搜索框的菜单项。可以使用菜单管理页面提供的搜索功能来定位到特定的菜单项。 3. 编辑菜单项。进入菜单项编辑页面后,你可以看到一些可用的选项。 4. 在"参数"字段中添加自定义搜索框的配置。在参数字段中,你可以添加一些额外的配置以实现自定义搜索框的功能。具体的配置方式取决于你的需求,以下是一个示例: - 类型(type):可以设置为`search`表示是一个搜索框。 - 名称(name):指定搜索框的名称。 - URL(url):指定搜索请求提交的URL地址。 - 方法(method):指定搜索请求提交的方法,例如GET或POST。 - 参数(param):指定额外的参数,例如可以传递当前页面的URL参数。 例如,可以添加如下配置: ``` type=search name=custom_search url=/admin/custom/search method=GET param=menu_id:{:id} ``` 5. 创建对应的搜索处理方法。根据上述配置中指定的URL地址,在FastAdmin框架的控制器中创建对应的方法来处理搜索请求。在该方法中,你可以根据参数进行搜索逻辑的实现。 例如,在控制器中创建`admin/CustomController`,并添加以下方法: ```php public function search() { $menuId = $this->request->param('menu_id'); // 进行自定义搜索逻辑处理 // ... } ``` 6. 保存菜单项。完成配置后,点击保存按钮来保存菜单项的修改。 现在,你就完成了在FastAdmin框架中自定义搜索框实现。请根据你的具体需求和框架版本进行配置和开发,以满足你的自定义搜索功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阮小鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值