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

xiaoxiao2021-02-27  301

最近刚辞职,找工作也是焦头烂额啊。大家都说,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(); } }
转载请注明原文地址: https://www.6miu.com/read-3895.html

最新回复(0)