ScrollView嵌套圆角Listview 实现同时滑动效果
2016-08-12 17:27
567 查看
免积分下载demo
效果图如下,我的模拟器异常,就看个静态图片把:
首先看主布局文件activity_main.xml
listview圆角secret_contact_bg.xml
MainActivity主界面代码
ElasticScrollView
CornerListView
contact_secret_listview_item.xml
说下需要注意的bug:
1、如果将来主布局文件改成如下的,listview直接套在scrollview下
2、布局不变,但是在主页代码中setListViewHeightBasedOnChildren(cornerListView);这个方法去掉
以上1、2种情况都会遇到同样的一个bug,就是listview只显示一行,listview没有测量高度
在ScrollView中嵌套ListView空间,无法正确的计算ListView的大小,故可以通过代码,根据当前的ListView的列表项计算列表的尺寸。
效果图如下,我的模拟器异常,就看个静态图片把:
首先看主布局文件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的列表项计算列表的尺寸。
相关文章推荐
- NestedScrollView 嵌套 ListView 实现滑动折叠效果
- 横向滑动的listview效果的实现方法,scrollview嵌套水平滑动的listview卡顿的解决方法
- 一个 ScrollView 里面包含 viewpager 嵌套 listview 或 RecyclerView 极少代码实现的流畅滑动效果 处理一个两层滑动 view 的自定义布局,以最少的代码实现,
- Scrollview中嵌套ViewPager中嵌套ListView 滑动中tab固定顶部ScrollingTricks效果特效
- 【Android界面实现】解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- ViewPager和Fragment结合,利用(HorizontalScrollView)实现指示器与ViewPager同时滑动的动态效果
- 【Android界面实现】解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- Scrollview中嵌套ViewPager中嵌套ListView 滑动中tab固定顶部ScrollingTricks效果特效
- 【Android界面实现】解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- 安卓学习笔记---实现两个ListView或者GridView在Scrollview中同时滑动,实现ScrollView滑动到顶部
- 自定义控件(24)---自定义控件之setMargins、嵌套ScrollView并且实现ListView阻尼效果
- 【Android界面实现】解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- 【Android界面实现】解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- Android ScrollView中嵌套ListView滚动效果冲突问题解决
- Android实现ListView圆角效果
- Android实现ListView圆角效果
- Android中实现ListView圆角效果[转]
- Android UI控件之ListView实现圆角效果
- ScrollView中若嵌套有滑动的控件(如:WebView,ListView或GridView),另外该界面中还有其它的控件时,界面不显示最上面控件的问题。
- android 自定义ScrollView实现反弹效果(以及解决和ListView之间的冲突)