ScrollRelativeLayout
有些页面我们需要除了TitleBar以外的区域整体可以随着手指滑动而滑动,松手后整体又会以相应interpolator的效果复位。
内部的4个Item是RelativeLayout布局。
问题点1: 当按下位置在内部的RelativeLayout上时滑动无效
问题点2: 滑动整体过后界面的位置复原
先看下这个自定义的RelativeLayout
public class ScrollRelativeLayout extends RelativeLayout {
private Context ct;
private boolean isMove;
public ScrollRelativeLayout(Context context) {
super(context);
ct = context;
initView();
}
public ScrollRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
ct = context;
initView();
}
public ScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ct = context;
initView();
}
private void initView() {
AnticipateInterpolator interpolator = new AnticipateInterpolator();
mScroller = new Scroller(ct, interpolator);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
isMove = true;
} else {
isMove = false;
}
super.computeScroll();
}
/**
* 点击时候Y的坐标
*/
private int downY = 0;
/**
* 拖动时候Y的坐标
*/
private int moveY = 0;
/**
* 松开时候Y的坐标
*/
private int upY = 0;
/**
* 拖动时候Y的方向距离
*/
private int scrollY = 0;
private Scroller mScroller;
@Override
public boolean onTouchEvent(MotionEvent event) {
int offViewY = 0;//屏幕顶部和该布局顶部的距离
if (!isMove) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("down");
downY = (int) event.getRawY();
return true;
case MotionEvent.ACTION_MOVE:
System.out.println("move");
moveY = (int) event.getRawY();
scrollY = moveY - downY;
// System.out.println(scrollY);
if (scrollY > 0) {/**向下拖动*/
scrollTo(0, -scrollY / 2);
} else {/**向上拖动*/
scrollTo(0, -scrollY / 2);
}
break;
case MotionEvent.ACTION_UP:
// System.out.println("up");
upY = (int) event.getRawY();
if (downY-upY > 0) {/**向上滑动*/
startAnimation(-scrollY/2, scrollY/2, 500);
} else/**向下滑动*/ {
startAnimation(-scrollY/2, scrollY/2, 500);
}
break;
}
}
return super.onTouchEvent(event);
}
int down1Y ;
int move1Y;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
down1Y = (int) ev.getRawY();
System.out.println("down1Y");
break;
case MotionEvent.ACTION_MOVE:
move1Y = (int) ev.getRawY();
System.out.println("move1");
if (Math.abs(move1Y - down1Y) > 10) {
downY=down1Y;
return true;
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
private void startAnimation(int startY, int dy, int duration) {
isMove = true;
mScroller.startScroll(0, startY, 0, dy, duration);
invalidate();
}
}
这里是布局文件的使用这个自定义布局
<com.youarenotin.bzgj.UI.Widgets.ScrollRelativeLayout
android:id="@+id/setting_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/mine_setting1">
<RelativeLayout
android:id="@id/setting_common_info"
android:layout_width="fill_parent"
android:layout_height="50.0dip"
android:layout_marginTop="30.0dip"
android:background="#ffffffff">
<TextView
style="@style/TextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="25.0dip"
android:layout_marginTop="20.0dip"
android:text="常用信息" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="10.0dip"
android:src="@drawable/more"
android:visibility="visible" />
</RelativeLayout>
<View
android:layout_width="fill_parent"
android:layout_height="0.100000024dip"
android:layout_below="@id/setting_common_info"
android:layout_marginLeft="20.0dip"
android:background="@drawable/hengchang" />
<RelativeLayout
android:id="@id/setting_Anewmessage_info"
android:layout_width="fill_parent"
android:layout_height="50.0dip"
android:layout_below="@id/setting_common_info"
android:layout_marginTop="0.100000024dip"
android:background="#ffffffff">
<TextView
style="@style/TextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="25.0dip"
android:layout_marginTop="20.0dip"
android:text="新消息提示" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="10.0dip"
android:src="@drawable/more"
android:visibility="visible" />
</RelativeLayout>
<View
android:layout_width="wrap_content"
android:layout_height="0.100000024dip"
android:layout_below="@id/setting_Anewmessage_info"
android:layout_marginLeft="20.0dip"
android:background="@drawable/hengchang" />
<RelativeLayout
android:id="@id/setting_pwd_security"
android:layout_width="fill_parent"
android:layout_height="50.0dip"
android:layout_below="@id/setting_Anewmessage_info"
android:layout_marginTop="0.100000024dip"
android:background="#ffffffff">
<TextView
style="@style/TextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="25.0dip"
android:layout_marginTop="20.0dip"
android:text="账户与安全" />
</RelativeLayout>
<View
android:layout_width="wrap_content"
android:layout_height="0.100000024dip"
android:layout_below="@id/setting_pwd_security"
android:layout_marginLeft="20.0dip"
android:background="@drawable/hengchang" />
<RelativeLayout
android:id="@id/setting_about"
android:layout_width="fill_parent"
android:layout_height="50.0dip"
android:clickable="false"
android:focusable="false"
android:layout_below="@id/setting_pwd_security"
android:layout_marginTop="0.100000024dip"
android:background="#ffffffff">
<TextView
style="@style/TextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="25.0dip"
android:layout_marginTop="20.0dip"
android:text="关于XXXX" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="10.0dip"
android:src="@drawable/more"
android:visibility="visible" />
</RelativeLayout>
<View
android:layout_width="wrap_content"
android:layout_height="0.100000024dip"
android:layout_below="@id/setting_about"
android:background="@drawable/hengchang" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="10.0dip"
android:src="@drawable/more"
android:visibility="invisible" />
</com.youarenotin.bzgj.UI.Widgets.ScrollRelativeLayout>
解决问题1: 这个问题涉及到触摸事件的拦截与消费
拦截事件 onInterceptTouchEvent(ev)这个方法是从触摸位置的顶层view向下层传递具体细节部分可参看Android-onInterceptTouchEvent()和onTouchEvent()总结
当点击非内部Item的区域时,直接由本类中的onTouchEvent来处理触摸事件。
这里返回true表示由本类来消费该次触摸事件,接下来的move和up都由本类onTouchEvent来处理。 getRawY()和getY()的区别是前者测量的是触点相对屏幕的距离,而后者测量的是相对于容器的位置坐标,因为滑动过程中我们需要让容器也跟着滑动所以不多说用getRawY()。
当点击内部Item时,由于我们在引用这个类时,为每个Item添加了onClickListener()的回调,所以onTouchEvent的事件传递被内部item消费了。股本类中onTouchEvent将收不到任何ev。但是当我们在点击的过程中稍微的滑动了一下这里面设置的阈值是10
if (Math.abs(move1Y - down1Y) > 10) {
downY=down1Y;
return true;
}
当在点击内部item的同时抖动的距离超过10个像素的话,我们认为该操作是要滑动而不是点击事件,故return true; 拦截事件的向下传递 ,由本类onTouchEvent处理。滑动处理部分在 move这个事件里,细节可参考 scrollto的用法。
解决问题二 : 滑动过程中松手后恢复事件在onTouchEvent中的ACTION_UP中处理,这里需要借助一个帮助类scroller,需要注意的是
private void startAnimation(int startY, int dy, int duration) {
isMove = true;
mScroller.startScroll(0, startY, 0, dy, duration);
invalidate();
}
中startY是相对于该空间最最原始的位置与移动过程无关并且它这个方向与我们常识中的坐标轴方向正好相反,好费解.
在invalidate()后会不断调用该view的computeScroll()方法
@Override
public void computeScroll() {
//计算是否还有需要移动的距离
if (mScroller.computeScrollOffset()) {//需要 继续用scroller是移动渲染界面
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
isMove = true;
} else {//不需要 ,移动完毕
isMove = false;
}
super.computeScroll();
}
总结: 小细节问题基本上就这些。其中有几个关键技术点需要仔细看一些大手的文章和demo才能明白。