13、自定义ListView
程序员文章站
2022-03-11 11:24:16
...
下拉刷新,上拉加载,很流行的啊,总的来说有两种。一种当ListView下拉时整个页面一块下移,露出顶部的提示,另一种当ListView下拉到最顶端时,页面不移动,而是在上面出现一层提示或图标。上拉加载都是判断页面有没有快要滑到底部。
注:第二种想贴知乎的下拉效果,无奈知乎在模拟器上怎么都装不上
这里来实现第二种。原理就是自定一种布局,包含一个ListView和头部的刷新提示,代码里就在触摸事件函数判断状态,改变刷新提示的显示位置、信息就可以。新建一个类继承自RelativeLayout(提示图标在中间而且位于ListView上层,所以选择RelativeLayout):
为了简单,新建了一个对应的布局文件,在类构造中引入布局文件,获取其中的控件(ListView和提示布局等)操作,而不是在类中用代码去新生成一个ListView和提示布局等(当然这也可以,各有利弊),layout_qlistview:
注:仅是包括了一个提示用的图标,并没有ListView,因为ListView要加头和尾并且要控制,方便点在代码里新生成的
QListView构造方法中:
引入了layout_qlistview,新生成CListView并加入到最底层,CListView为一个自定义内部类,继承自ListView。
QListView初始化相关函数:
QListView还要实现(加入)一些ListView常用的方法,以使QListView和ListView用法一致,如:
将上节中的ListView都改为QListView(布局要写全路径),运行,确定不会崩溃。
注:这是一个.gif动图,ctrl点击图片查看,布局改过,之后会说
接着,实现滑动时的图标提示,触摸事件处理dispatchTouchEvent中:
代码比较乱,CListView添加了个头和尾,如下:
头尾布局如下:
view_listheader
view_listfooter
运行效果:
注:头部的重影不知是怎么回事,真机是正常的,拉下来后并不会自动弹回去
注:第二种想贴知乎的下拉效果,无奈知乎在模拟器上怎么都装不上
这里来实现第二种。原理就是自定一种布局,包含一个ListView和头部的刷新提示,代码里就在触摸事件函数判断状态,改变刷新提示的显示位置、信息就可以。新建一个类继承自RelativeLayout(提示图标在中间而且位于ListView上层,所以选择RelativeLayout):
public class QListView extends RelativeLayout { public QListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } }
为了简单,新建了一个对应的布局文件,在类构造中引入布局文件,获取其中的控件(ListView和提示布局等)操作,而不是在类中用代码去新生成一个ListView和提示布局等(当然这也可以,各有利弊),layout_qlistview:
<?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="match_parent" > <RelativeLayout android:id="@+id/rl_refresh" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="-42dp" > <ImageView android:id="@+id/iv_refresh" android:layout_width="38dp" android:layout_height="38dp" android:layout_centerHorizontal="true" android:padding="8dp" android:background="@drawable/shaper_oval_apptheme" android:src="@drawable/refresh" android:tint="@color/white_dark" android:rotation="90"/> </RelativeLayout> </RelativeLayout>
注:仅是包括了一个提示用的图标,并没有ListView,因为ListView要加头和尾并且要控制,方便点在代码里新生成的
QListView构造方法中:
public QListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.layout_qlistview, this); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT); clv_list=new CListView(context); clv_list.setLayoutParams(lp); this.addView(clv_list, 0); initView(context); initListener(); }
引入了layout_qlistview,新生成CListView并加入到最底层,CListView为一个自定义内部类,继承自ListView。
private class CListView extends ListView { public CListView(Context context) { super(context); // TODO Auto-generated constructor stub } //触摸事件处理,放到ListView里,放到外层的RelativeLayout不知道可不可以。 public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated constructor stub return super.dispatchTouchEvent(ev); } }
QListView初始化相关函数:
private void initView(Context context){ iv_refresh=(ImageView)findViewById(R.id.iv_refresh); rl_refresh=(RelativeLayout)findViewById(R.id.rl_refresh); rl_refresh.setPadding(0, -200, 0, 0); anim_rotate=AnimationUtils.loadAnimation(context, R.anim.anim_rotate_round);//先不管它 clv_list.setDividerHeight(0); clv_list.setSelector(R.drawable.shaper_rect_null); } private void initListener(){ }
QListView还要实现(加入)一些ListView常用的方法,以使QListView和ListView用法一致,如:
public void setAdapter(ListAdapter adapter){ this.clv_list.setAdapter(adapter); }
将上节中的ListView都改为QListView(布局要写全路径),运行,确定不会崩溃。
注:这是一个.gif动图,ctrl点击图片查看,布局改过,之后会说
接着,实现滑动时的图标提示,触摸事件处理dispatchTouchEvent中:
switch(ev.getAction()){ case MotionEvent.ACTION_DOWN: if(getFirstVisiblePosition()==0&&!isRefresh){ isDown=true; downY=ev.getY(); } break; case MotionEvent.ACTION_MOVE: if(isDown){ float dY=ev.getY()-downY; dY/=1.414f; int maxH=header.getHeight()-iv_refresh.getHeight()/2; float scale=dY/maxH; if(dY>0){ if(rl_refresh.getPaddingTop()<=maxH){ if(scale<2){ iv_refresh.setRotation(scale*360); iv_refresh.setAlpha(scale*1.414f); if(dY-iv_refresh.getHeight()<maxH){ rl_refresh.setPadding(0, (int)dY-iv_refresh.getHeight(), 0, 0); } else{ rl_refresh.setPadding(0, maxH, 0, 0); } } } isMove=true; return true; } else{ if(isMove){ return true; } } } break; default: isDown=false; if(isMove){ int temp=header.getHeight()+iv_refresh.getHeight()/2; if(ev.getY()-downY>temp*1.2f){ iv_refresh.setRotation(360); iv_refresh.setAlpha(1.0f); rl_refresh.setPadding(0, temp-iv_refresh.getHeight(), 0, 0); isPull=true; } if(isPull){ isPull=false; iv_refresh.startAnimation(anim_rotate); if(pulllistener!=null){//刷新监听器,下节会说 pulllistener.onRefresh(); isRefresh=true; } } else{ rl_refresh.setPadding(0, -iv_refresh.getHeight(), 0, 0); } isMove=false; return true; } break; }
代码比较乱,CListView添加了个头和尾,如下:
public CListView(Context context) { super(context); // TODO Auto-generated constructor stub initView(context); initListener(); } private void initView(Context context){ this.addHeaderView(new View(context));//添加空,便于操作 header=new CHeader(context);//头部,见下 footer=new CFooter(context);//尾部,见下 this.addHeaderView(header,null,false);//添加 this.addFooterView(footer, null, false); } private class CHeader extends RelativeLayout{ public CHeader(Context context) { super(context); // TODO Auto-generated constructor stub LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.view_listheader, this); } } private class CFooter extends RelativeLayout{ public CFooter(Context context) { super(context); // TODO Auto-generated constructor stub LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.view_listfooter, this); } }
头尾布局如下:
view_listheader
<?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="wrap_content" android:paddingTop="@dimen/padding_s" android:paddingLeft="@dimen/padding_s" android:paddingRight="@dimen/padding_s" android:paddingBottom="0.5dp" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/padding_ll" android:paddingBottom="@dimen/padding_ll" android:paddingLeft="@dimen/padding_n" android:paddingRight="@dimen/padding_n" android:background="@color/white_dark" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:textSize="@dimen/text_level2" android:textColor="@color/text_level3" android:text="列表:全部" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:textSize="@dimen/text_level2" android:textColor="@color/text_level3" android:text="最近更新:23:22" /> </RelativeLayout> </RelativeLayout>
view_listfooter
<?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="wrap_content" android:paddingTop="0dp" android:paddingLeft="@dimen/padding_s" android:paddingRight="@dimen/padding_s" android:paddingBottom="@dimen/padding_s" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/padding_ll" android:paddingBottom="@dimen/padding_ll" android:paddingLeft="@dimen/padding_n" android:paddingRight="@dimen/padding_n" android:background="@color/white_dark" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:textSize="@dimen/text_level2" android:textColor="@color/text_level3" android:text="加载中•••" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:textSize="@dimen/text_level2" android:textColor="@color/text_level3" android:text="已加载:36条" /> </RelativeLayout> </RelativeLayout>
运行效果:
注:头部的重影不知是怎么回事,真机是正常的,拉下来后并不会自动弹回去
重新启动——2017/05/15
上一篇: 工作经验,冷笑话