您的位置:首页 > 其它

ScrollView嵌套圆角Listview 实现同时滑动效果

2016-08-12 17:27 567 查看
免积分下载demo

效果图如下,我的模拟器异常,就看个静态图片把:





首先看主布局文件activity_main.xml

<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.scrollview.MainActivity" >

<com.example.scrollview.ElasticScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >

<com.example.scrollview.CornerListView
android:id="@+id/contact_secret_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dip"
android:background="@drawable/secret_contact_bg"
android:cacheColorHint="#00000000"
android:divider="@null"
android:dividerHeight="0dp"
android:scrollbars="none" />

</LinearLayout>

</com.example.scrollview.ElasticScrollView>

</RelativeLayout>


listview圆角secret_contact_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/a" />

<corners android:bottomLeftRadius="5dip"
android:bottomRightRadius="5dip" android:topLeftRadius="5dip"
android:topRightRadius="5dip" />

<stroke android:color="@color/dividerColor" android:width="0.5dp"></stroke>
</shape>

<color name="a">#ffffff</color>
<color name="dividerColor">#cccccc</color>


MainActivity主界面代码

package com.example.scrollview;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {

CornerListView cornerListView;
private MyAdapter myAdapter;
String[] datas = { "aaa", "bbb", "ccc", "ddd", "eee" };

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

cornerListView = (CornerListView) findViewById(R.id.contact_secret_listview);

// adapter
myAdapter = new MyAdapter();

cornerListView.setAdapter(myAdapter);
setListViewHeightBasedOnChildren(cornerListView);

}

public void setListViewHeightBasedOnChildren(ListView listView) {
// 获取ListView对应的Adapter
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}

int totalHeight = 0;
for (int i = 0, len = listAdapter.getCount(); i < len; i++) {
// listAdapter.getCount()返回数据项的数目
View listItem = listAdapter.getView(i, null, listView);
// 计算子项View 的宽高
listItem.measure(0, 0);
// 统计所有子项的总高度
totalHeight += listItem.getMeasuredHeight();
}

ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
// listView.getDividerHeight()获取子项间分隔符占用的高度
// params.height最后得到整个ListView完整显示需要的高度
listView.setLayoutParams(params);
}

class MyAdapter extends BaseAdapter {

LayoutInflater inflater;

public MyAdapter() {
inflater = LayoutInflater.from(MainActivity.this);
}

@Override
public int getCount() {
// TODO Auto-generated method stub
return datas.length;
}

@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return datas[position];
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(
R.layout.contact_secret_listview_item, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

viewHolder.secretContactName.setText(datas[position]);
return convertView;
}

class ViewHolder {
private TextView secretContactName;

ViewHolder(View view) {
secretContactName = (TextView) view
.findViewById(R.id.secret_contact_name);
}
}

}
}


ElasticScrollView

package com.example.scrollview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
* ScrollView反弹效果的实现
*/
public class ElasticScrollView extends ScrollView {
private View inner;// 子View

private float y;// 点击时y坐标

private Rect normal = new Rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.)

private boolean isCount = false;// 是否开始计算

@SuppressLint("NewApi")
public ElasticScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}

/***
* 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate
* 方法,也应该调用父类的方法,使该方法得以执行.
*/

@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 0) {
inner = getChildAt(0);
}
}

/***
* 监听touch
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (inner != null) {
commOnTouchEvent(ev);
}
return super.onTouchEvent(ev);
}

/***
* 触摸事件
*
* @param ev
*/
public void commOnTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
// 手指松开.
if (isNeedAnimation()) {
animation();
isCount = false;
}
break;
/**
* 排除出第一次移动计算,因为第一次无法得知y坐标, 在MotionEvent.ACTION_DOWN中获取不到,
* 因为此时是MyScrollView的touch事件传递到到了LIstView的孩子item上面.所以从第二次计算开始.
* 然而我们也要进行初始化,就是第一次移动的时候让滑动距离归0. 之后记录准确了就正常执行.
*/
case MotionEvent.ACTION_MOVE:
final float preY = y;// 按下时的y坐标
float nowY = ev.getY();// 时时y坐标
int deltaY = (int) (preY - nowY);// 滑动距离
if (!isCount) {
deltaY = 0; // 在这里要归0.
}

y = nowY;
// 当滚动到最上或者最下时就不会再滚动,这时移动布局
if (isNeedMove()) {
// 初始化头部矩形
if (normal.isEmpty()) {
// 保存正常的布局位置
normal.set(inner.getLeft(), inner.getTop(),inner.getRight(), inner.getBottom());
}
// 移动布局
inner.layout(inner.getLeft(), inner.getTop() - deltaY / 2,inner.getRight(), inner.getBottom() - deltaY / 2);
}
isCount = true;
break;

default:
break;
}
}

/***
* 回缩动画
*/
public void animation() {
// 开启移动动画
TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),normal.top);
ta.setDuration(200);
inner.startAnimation(ta);
// 设置回到正常的布局位置
inner.layout(normal.left, normal.top, normal.right, normal.bottom);

normal.setEmpty();

}

// 是否需要开启动画
public boolean isNeedAnimation() {
return !normal.isEmpty();
}

/***
* 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度
*
* getHeight():获取的是屏幕的高度
*
* @return
*/
public boolean isNeedMove() {
int offset = inner.getMeasuredHeight() - getHeight();
int scrollY = getScrollY();
// 0是顶部,后面那个是底部
if (scrollY == 0 || scrollY == offset) {
return true;
}
return false;
}
}


CornerListView

package com.example.scrollview;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ExpandableListView;
import android.widget.ListView;

public class CornerListView extends ListView implements Runnable {

// 手指点位置的Y坐标
private float mLastDownY = 0f;
// 移动距离
private int mDistance = 0;
private int mStep = 0;
// 是否移动过
private boolean mPositive = false;

/**
* 构造器
*/
public CornerListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

public CornerListView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public CornerListView(Context context) {
super(context);
}

/**
* TouchEvent事件
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 系列事件,手指第一次按下时触发
if (mLastDownY == 0f && mDistance == 0) {
mLastDownY = event.getY();
return true;
}
break;

case MotionEvent.ACTION_CANCEL:
break;

case MotionEvent.ACTION_UP:
// 手指离开之后触发
if (mDistance != 0) {
mStep = 1;
mPositive = (mDistance >= 0);
// 即可把你的Runnable对象增加到UI线程中运行。
this.post(this);
return true;
}
// 重新赋值
mLastDownY = 0f;
mDistance = 0;
break;

case MotionEvent.ACTION_MOVE: // 手指按下之后滑动触发
if (mLastDownY != 0f) {
mDistance = (int) (mLastDownY - event.getY());
if ((mDistance < 0 && getFirstVisiblePosition() == 0 && getChildAt(
0).getTop() == 0)
|| (mDistance > 0 && getLastVisiblePosition() == getCount() - 1)) {
// 第一个位置并且是想下拉,就滑动或者最后一个位置向上拉
// 这个判断的作用是在非顶端的部分不会有此滚动
mDistance /= 3; // 这里是为了减少滚动的距离
scrollTo(0, mDistance); // 滚动
return true;
}
}
// 置为0,有自动滑动的效果
mDistance = 0;
break;
}
return super.onTouchEvent(event);
}

public void run() {
mDistance += mDistance > 0 ? -mStep : mStep;
scrollTo(0, mDistance);
// 下拉mPositive是false,上拉是true
if ((mPositive && mDistance <= 0) || (!mPositive && mDistance >= 0)) {
scrollTo(0, 0);
mDistance = 0;
mLastDownY = 0f;
return;
}
mStep += 1;
this.post(this);
}
}


contact_secret_listview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="vertical">
<TextView
android:id="@+id/secret_contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="name"
android:layout_marginLeft="20dp" />

<View
android:layout_alignParentBottom="true"
android:background="#cccccc"
android:layout_marginLeft="14.333dp"
android:layout_width="match_parent"
android:layout_height="0.5dp"></View>
</RelativeLayout>


说下需要注意的bug:

1、如果将来主布局文件改成如下的,listview直接套在scrollview下

<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.scrollview.MainActivity" >

<com.example.scrollview.ElasticScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >

<com.example.scrollview.CornerListView
android:id="@+id/contact_secret_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dip"
android:background="@drawable/secret_contact_bg"
android:cacheColorHint="#00000000"
android:divider="@null"
android:dividerHeight="0dp"
android:scrollbars="none" />

</com.example.scrollview.ElasticScrollView>

</RelativeLayout>


2、布局不变,但是在主页代码中setListViewHeightBasedOnChildren(cornerListView);这个方法去掉

以上1、2种情况都会遇到同样的一个bug,就是listview只显示一行,listview没有测量高度

在ScrollView中嵌套ListView空间,无法正确的计算ListView的大小,故可以通过代码,根据当前的ListView的列表项计算列表的尺寸。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐