自定义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();
}
}
相关文章推荐
- 自定义布局实现头部搜索框
- 【安卓-自定义布局】安卓App开发思路 一步一个脚印(十一)实现自定义左右滚动的导航栏目--仿美团
- 泡泡窗口实现下拉菜单,以及改变布局高度
- 自定义View实现阶梯梯形布局以及二维码的实现
- js代码实现根据实际物理地址,进行 URL 跳转,并实现用户自定义 URL 跳转
- RecyclerView实现滚动滑动以及点击改变控件颜色
- 基于Spark的移动用户主要活动地点的挖掘算法实现以及JavaEE技术整合
- RecyclerView实现滚动滑动以及点击改变控件颜色
- 聊天功能,实现:每增加一条消息整体往上移动,滚动查看聊天记录
- 根据仿人人客户端教程,编程实现Demo(二)---实现JSON解析人人API2.0,获取用户信息以及新鲜事信息
- Android -- 自定义实现横竖双向滚动的列表(ListView)布局
- rem布局实现不同分辨率移动终端的自适应、整体缩放
- iOS 根据父视图布局的方法 以及实现 Scrollview 拉动 遮罩输入框问题解决
- 实现控件移动(三)--改变控件布局参数
- Android自定义ViewGroup实现可滚动的横向布局(2)
- CoordubatirLayout简单使用2,移动自定义布局改变动画
- js代码实现根据实际物理地址,进行 URL 跳转,并实现用户自定义 URL 跳转
- Android——自定义view实现标题栏、梯形布局以及二维码扫描
- 页面头部和左侧固定并撑满,只有右侧部分内容改变的布局实现
- AlertDialog实现自定义布局以及解决EditText无法弹出软键盘或者软键盘弹出错误问题