ScrollView嵌套ListVew或者GridView等很常用,下面来说解决版本 问题一 : 嵌套在 ScrollView的 ListVew数据显示不全,我遇到的是最多只显示两条已有的数据。 解决办法:重写 ListVew或者 GridView,网上还有很多若干解决办法,但是都不好用或者很复杂。
问题二 、打开套有 ListVew的 ScrollView的页面布局 默认 起始位置不是最顶部。 解决办法有两种都挺好用: 一是把套在里面的Gridview 或者 ListVew 不让获取焦点即可。 gridview.setFocusable(false); listview.setFocusable(false); 注意:在xml布局里面设置android:focusable=“false”不生效 方法二:网上还查到说可以设置myScrollView.smoothScrollTo(0,0); |
看错误的布局代码:
android:id="@+id/act_solution_1_sv"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="\nListView上方数据\n" />
android:id="@+id/act_solution_1_lv"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="\nListView下方数据\n" />
ScrollView中只能放一个控件,一般都放LinearLayout,orientation属性值为vertical。
在LinearLayout中放需要呈现的内容。ListView也在其中,ListView的高度设为适应自身内容(wrap_content
原因就是scroll事件的消费处理以及ListView控件的高度设定问题
嵌套是ViewPager-->ScrollView-->ViewPager.
public class HorizontalInnerViewPager extends ViewPager {
/** 触摸时按下的点 **/
PointF downP = new PointF();
/** 触摸时当前的点 **/
PointF curP = new PointF();
/** 自定义手势**/
private GestureDetector mGestureDetector;
public HorizontalInnerViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, new XScrollDetector());
}
public HorizontalInnerViewPager(Context context) {
super(context);
mGestureDetector = new GestureDetector(context, new XScrollDetector());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);//default
//当拦截触摸事件到达此位置的时候,返回true,
//说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent
// return true;
//接近水平滑动时子控件处理该事件,否则交给父控件处理
// return mGestureDetector.onTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//每次进行onTouch事件都记录当前的按下的坐标
curP.x = ev.getX();
curP.y = ev.getY();
if(ev.getAction() == MotionEvent.ACTION_DOWN){
//记录按下时候的坐标
//切记不可用 downP = curP ,这样在改变curP的时候,downP也会改变
downP.x = ev.getX();
downP.y = ev.getY();
//此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
getParent().requestDisallowInterceptTouchEvent(true);
}
if(ev.getAction() == MotionEvent.ACTION_MOVE){
float distanceX = curP.x - downP.x;
float distanceY = curP.y - downP.y;
//接近水平滑动,ViewPager控件捕获手势,水平滚动
if(Math.abs(distanceX) > Math.abs(distanceY)){
//此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
getParent().requestDisallowInterceptTouchEvent(true);
}else{
//接近垂直滑动,交给父控件处理
getParent().requestDisallowInterceptTouchEvent(false);
}
}
return super.onTouchEvent(ev);
}
private class XScrollDetector extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// return super.onScroll(e1, e2, distanceX, distanceY);
//接近水平滑动时子控件处理该事件,否则交给父控件处理
return (Math.abs(distanceX) > Math.abs(distanceY));
}
}
}
两个相同方向的ScrollView是不能嵌套的,所以尽量避免这种情况; 如果不能避免,则看下面
目前做的这个只支持两个ScrollView嵌套,两个以上还有待改进,能套两个就已经能满足很多需求了目前为纵向scrollview的支持.直接看代码:
public class InnerScrollView extends ScrollView {
/**
*/
public ScrollView parentScrollView;
public InnerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private int lastScrollDelta = 0;
public void resume() {
overScrollBy(0, -lastScrollDelta, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);
lastScrollDelta = 0;
}
int mTop = 10;
/**
* 将targetView滚到最顶端
*/
public void scrollTo(View targetView) {
int oldScrollY = getScrollY();
int top = targetView.getTop() - mTop;
int delatY = top - oldScrollY;
lastScrollDelta = delatY;
overScrollBy(0, delatY, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);
}
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0, child.getHeight() - (getHeight()));
}
return scrollRange;
}
int currentY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (parentScrollView == null) {
return super.onInterceptTouchEvent(ev);
} else {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 将父scrollview的滚动事件拦截
currentY = (int)ev.getY();
setParentScrollAble(false);
return super.onInterceptTouchEvent(ev);
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
// 把滚动事件恢复给父Scrollview
setParentScrollAble(true);
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
View child = getChildAt(0);
if (parentScrollView != null) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
int height = child.getMeasuredHeight();
height = height - getMeasuredHeight();
// System.out.println("height=" + height);
int scrollY = getScrollY();
// System.out.println("scrollY" + scrollY);
int y = (int)ev.getY();
// 手指向下滑动
if (currentY < y) {
if (scrollY <= 0) {
// 如果向下滑动到头,就把滚动交给父Scrollview
setParentScrollAble(true);
return false;
} else {
setParentScrollAble(false);
}
} else if (currentY > y) {
if (scrollY >= height) {
// 如果向上滑动到头,就把滚动交给父Scrollview
setParentScrollAble(true);
return false;
} else {
setParentScrollAble(false);
}
}
currentY = y;
}
}
return super.onTouchEvent(ev);
}
/**
* 是否把滚动事件交给父scrollview
*
* @param flag
*/
private void setParentScrollAble(boolean flag) {
parentScrollView.requestDisallowInterceptTouchEvent(!flag);
}
}
在 Android 应用中,大部分情况下都会使用一个垂直滚动的 View 来显示内容(比如 ListView、RecyclerView 等)。但是有时候你还希望垂直滚动的View 里面的内容可以水平滚动。如果直接在垂直滚动的 View 里面使用水平滚动的 View,则滚动操作并不是很流畅。 比如下图中的示例: 为什么会出现这个问题呢?上图中的布局为一个 RecyclerView 使用的是垂直滚动的 LinearLayoutManager 布局管理器,而里面每个 Item 为另外一个 RecyclerView 使用的是水平滚动的 LinearLayoutManager。而在 Android系统的事件分发 中,即使最上层的 View 只能垂直滚动,当用户水平拖动的时候,最上层的 View 依然会拦截点击事件。下面是 RecyclerView.java 中 onInterceptTouchEvent 的相关代码: 注意上面的 if 判断: RecyclerView 并没有判断用户拖动的角度, 只是用来判断拖动的距离是否大于滚动的最小尺寸。 如果是一个只能垂直滚动的 View,这样实现是没有问题的。如果我们在里面再放一个 水平滚动的 RecyclerView ,则就出现问题了。 可以通过如下的方式来修复该问题: 下面是一个完整的实现 BetterRecyclerView.java : 其他问题当用户快速滑动(fling)RecyclerView 的时候, RecyclerView 需要一段时间来确定其最终位置。 如果用户在快速滑动一个子的水平 RecyclerView,在子 RecyclerView 还在滑动的过程中,如果用户垂直滑动,则是无法垂直滑动的。原因是子 RecyclerView 依然处理了这个垂直滑动事件。 所以,在快速滑动后的滚动到静止的状态中,子 View 不应该响应滑动事件了,再次看看 RecyclerView 的 onInterceptTouchEvent() 代码: 可以看到,当 RecyclerView 的状态为 SCROLL_STATE_SETTLING (快速滑动后到滑动静止之间的状态)时, RecyclerView 告诉父控件不要拦截事件。 同样的,如果只有一个方向固定,这样处理是没问题的。 针对我们这个嵌套的情况,父 RecyclerView 应该只拦截垂直滚动事件,所以可以这么修改父 RecyclerView: 下图为最终的结果: |
scrollview 嵌套recyclerview 时,recyclerview不显示,这就需要我们自己计算recyclerview的高度,比如:
这中方法适合item高度比较好计算的情形,但要遇到里面的item高度不一定这就需要我们重写recyclerview的高度了,以前嵌套listview的时候我们只需重写listview 然后重写
但是这种方法在recyclerview重写不管用。 我们此时要重写的的是LinearLayoutManager或GridLayoutManager
重写完之后,用就好说了,在adapter的onBindview和平常一样用就可以了
或者再activity中设置一样的 |
**
* 屏蔽 滑动事件
* Created by fc on 2015/7/16.
*/
public class MyScrollview extends ScrollView {
private int downX;
private int downY;
private int mTouchSlop;
public MyScrollview(Context context) {
super(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public MyScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) e.getRawX();
downY = (int) e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) e.getRawY();
if (Math.abs(moveY - downY) > mTouchSlop) {
return true;
}
}
return super.onInterceptTouchEvent(e);
}
}