下面ListView向上滚动时,先让上面的图片往上滚动,直到图片看不到时再让下面的ListView向上滚动。
当向下滑动时,先让ListView往下滑动,直到ListView不能往下滑动时在让上面图片往下滚动。
实现效果如下:
布局示意图
布局代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.meituanview.MainActivity" >
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop"
android:src="@drawable/bag" />
<LinearLayout
android:id="@+id/butsContainer"
android:layout_width="match_parent"
android:layout_height="@dimen/buts_container_height"
android:background="#e1e1e1"
android:orientation="horizontal" >
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:text="点菜" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:text="评价" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:text="商家" />
</LinearLayout>
<com.example.meituanview.FloatListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="400dp"
>
</com.example.meituanview.FloatListView>
</LinearLayout>
</RelativeLayout>
package com.example.meituanview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.ScrollView;
public class FloatListView extends ListView {
public FloatListView(Context context) {
super(context);
init();
}
public FloatListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FloatListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@SuppressLint("NewApi")
public FloatListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
}
private int lastY;
private int canScrollDistance; //可以滚动的最大距离,此处规定为布局中的ImageView的高度,可以根据需要修改
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction()==MotionEvent.ACTION_DOWN) {
lastY=(int) ev.getRawY();
canScrollDistance=((ViewGroup)getParent()).getChildAt(0).getHeight();
System.out.println(canScrollDistance);
return super.onTouchEvent(ev);
}
int currentY=(int) ev.getRawY();//一定要使用int型,否则误差会累积,导致慢慢往上滚动,当ImageView刚看不到时,ListView会突然上滚一段距离
if (currentY<lastY) {
View view=((ViewGroup)getParent());
if (view.getScrollY()<canScrollDistance) {
view.scrollBy(0, (int) (lastY-currentY));
if (view.getScrollY()>canScrollDistance) {//防止迅速滑动时,界面滚动距离过大
view.scrollTo(0, canScrollDistance);
}
lastY=currentY;
return true;
}
}else if (getFirstVisiblePosition()==0&&getChildAt(0).getTop()==0 ) {
View view=((ViewGroup)getParent());
if (view.getScrollY()>0) {
view.scrollBy(0, (int) (lastY-currentY));
lastY=currentY;
if (view.getScrollY()<0) {//防止迅速滑动时,界面滚动距离过大
view.scrollTo(0, 0);
}
return true;
}
}
lastY=(int) ev.getRawY();
return super.onTouchEvent(ev);// 调用super.onTouchEvent会导致ListView滚动
}
}
public class MainActivity extends Activity {
FloatListView floatListView;
LinearLayout container;
ViewGroup butsContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
floatListView=(FloatListView) findViewById(R.id.listview);
container=(LinearLayout) findViewById(R.id.container);
butsContainer=(ViewGroup) findViewById(R.id.butsContainer);
WindowManager wm = this.getWindowManager();
int height = wm.getDefaultDisplay().getHeight();
int butsContainerHeight=(int) getResources().getDimension(R.dimen.buts_container_height);
LinearLayout.LayoutParams params=
new LinearLayout.LayoutParams(
android.widget.LinearLayout.LayoutParams.MATCH_PARENT
, height-butsContainerHeight-getStatusBarHeight());
floatListView.setLayoutParams(params);
floatListView.setAdapter(new MyAdapter());
}
protected int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return 30;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println(position+" "+convertView);
if (convertView==null) {
ViewGroup v=(ViewGroup) LayoutInflater.from(getApplicationContext()).inflate(R.layout.item , floatListView,false);
((TextView)v.getChildAt(1)).setText("null.."+position);
return v;
}
ViewGroup v=(ViewGroup)convertView;
((TextView)v.getChildAt(1)).setText(""+position);
return convertView;
}
}
}
dimens.xml
<dimen name="buts_container_height">50dp</dimen>
实现原理:
首先让最外面的LinearLayout包裹子控件,然后要设置FloatListView的高度,目的是让最外面的LinearLayout的高度大于屏幕的高度,这样调用最外面的LinearLayout的scrollBy方法时才能让布局正确滚动。
FloatListView中重写 public boolean onTouchEvent(MotionEvent ev) ,在这个方法内部根据手指滑动方向以及最外面的父控件滚动的距离和FloatListView中第一个可见item的位置来确定到底是最外面父控件滚动还是FloatListView滚动,若是最外面的父控件需要滚动,则手动调用最外面父控件的scrollBy 方法让父控件滚动,否则直接调用super.onTouchEvent(ev);则可以让FloatListView滚动。
原理介绍完了,但要实现上面截图中的效果需要借助StickyListHeaders https://github.com/emilsjolander/StickyListHeaders 这个控件
我们只需要让这个类库中的WrapperViewList继承自我们的FloatListView,并在StickyListHeadersListView中添加 这个方法
public FloatListView getFloatListView(){
return mList;
}
然后这样调用
se.emilsjolander.stickylistheaders.StickyListHeadersListView listview=new StickyListHeadersListView(getContext());
listview.setAdapter(new TestBaseAdapter(getContext()));
floatListView= listview.getFloatListView();
floatListView.setScrollParent(scrollParent);
floatListView.setScrollParentScrollDistance(distance);
下面附上两个工程的代码,一个是不需要依赖第三方类库 的FloatListView简单实现原理工程,另一个是集成了 .StickyListHeadersListView 的,并通过Fragment实现的工程
链接: http://pan.baidu.com/s/1i4hI6s1 密码: 5vmb
链接: http://pan.baidu.com/s/1i4jerop 密码: h9q8