ListView常用拓展
来源:互联网 发布:手机淘宝如何延长收货 编辑:程序博客网 时间:2024/06/03 15:47
ListView虽然使用广泛,但系统原生的ListView显然是不能满足用户再审美、功能上不断提高需求。不过也不要紧,Android完全可以定制化,让我们非常方便的对原生ListView进行拓展、修改。于是,再开发者的创新下,ListView原来越丰富多彩,各种各样基于原生ListView的拓展让人目不暇接。下面来看几个常用的ListView拓展。
1具有弹性的ListView
Android默认的ListView再滚动到顶端或低端的时候,并没有很友好的提示。再Android5.X中,Google为这样的行为添加了一个班月行的阴影效果。
而在IOS系统中,列表都是具有弹性的,即滚动到低端或者顶端后会继续往下或者晚上滑动一段距离。不得不说,这样的设计的确更加的有友好。我们可以自己修改ListView,让ListView也可以“弹性十足”。
网上有很多通过重写ListView来实现弹性效果的方法,比如增加HeaderView或者使用ScrollView进行嵌套,方法很多,不过这个可以使用一种非常简单的方法来实现这个效果。虽然不如那些方法可定制化高,效果丰富,但主要目的是让我们如何从源代码中找到问题的解决办法。
再查看ListView源代码的时候可以发现,ListView中有个控制滑动到边缘的处理方法,如下所示。
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)
可以看见这样一个参数:maxOverScrollY,注释中这样写道----Number of pixels to overscroll by in either direction along the Y axis。由此可见,虽然它的默认值是0,但其实只要修改这个参数的值,就可以让ListView具有弹性了!所以,既然我们不知道为什么Google不采用这样的修改,那我们就自己来修改以下吧。重写这个方法,并将maxOverScrollY改为设置的值----mMaxOverDistance,代码如下所示。
@Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverDistance, isTouchEvent); }
这样,通过修改这个值,就实现了具有弹性的ListView了。当然,为了能够满足多分辨率的需求,我们可以再修改maxOverScrollY值的时候,可以通过屏幕的density来计算具体的值,让不同分辨率的弹性距离基本一直。
private void initView(){ DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); float density = metrics.density; mMaxOverDistance = (int) density * mMaxOverDistance; }
2.自动显示、隐藏布局的ListView
相信很多朋友都非常熟悉这样一个效果:当我们再ListView上滑动的时候顶部的ActionBar或ToolBar就会相应的隐藏或显示。
下面我们就来仿照这样的例子设计一个类似的效果。
我们知道,让一个布局显示或者隐藏并带有动画效果,可以通过属性动画很方便的实现,所以这个效果的关键在于如何获得ListView的各种滑动时间。所以借助View的onTouchListener接口来监听ListView的滑动,通过比较与上次坐标的大小,来判断滑动的方向,并通过滑动的方向来判断是否需要显示或者隐藏对应的布局。再开始判断滑动时间之前,我们还需要做一些准备工作,闪现需要给ListView增加一个HeaderView,避免第一个Item被Toolbar遮挡,代码如下所示。
private View getHeadView(){ View header = new View(this); header.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, (int)getResources().getDimension(R.dimen.abc_action_bar_default_height_material))); return header; }
再代码中,通过使用abc_action_bar_default_height_material属性获取系统ActionBar的高度,并设置给HeaderView。另外,定义一个mTouchSlop变量用来获取系统认为的最低滑动距离,即超过这个移动距离,系统就将其定义为滑动状态了。对于这个值的获取非常简单,代码如下所示。
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
有了前面的准备工作,下面我们就可以判断滑动的事件了,关键代码如下所示。
private float mFirstY; private float mCurrentY; private boolean mShow; private ObjectAnimator mAnimtor; private View.OnTouchListener touchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mFirstY = event.getY(); break; case MotionEvent.ACTION_MOVE: mCurrentY = event.getY(); int direction = 0; if(mCurrentY - mFirstY > mTouchSlop){ direction = 0;//down }else if(mFirstY - mCurrentY > mTouchSlop){ direction = 1;//up } if(direction == 1){ if(mShow){ toolbarAnim(0);//hide mShow = ! mShow; } } else if(direction == 0) { if(!mShow){ toolbarAnim(1);//show mShow = ! mShow; } } break; case MotionEvent.ACTION_UP: } return false; } };
代码逻辑非常简单,只是通过滑动的点坐标改变大小,来判断移动的方向,并根据移动方向来执行不同的动画效果。
有了前面的分析,实现一个效果就非常简单了,最后加上控制布局隐藏的动画,如下所示。
private void toolbarAnim(int flag) { if(mAnimtor != null && mAnimtor.isRunning()){ mAnimtor.cancel(); } if(flag == 0){ mAnimtor = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), 0); } else { mAnimtor = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), -toolbar.getHeight()); } mAnimtor.start(); }
动画也是最简单的位移属性动画。不过这里需要说一点题外话,这里使用了Toolbar这一一个新空间,Google已经推荐它用来逐渐取代ActionBar了,因为它更加灵活。但是再使用的时候,一定要注意使用的theme一定要NoActionBar的,不然会引起冲突。
3.聊天ListView
通常我们使用的ListView的每一项都具有相同的布局,所以展现出来的时候,除了数据不同,只要你不隐藏布局,其他的布局应该都是类似的。而我们熟知的qq等聊天APP,再聊天界面,会展示至少两种布局,即收到的消息和自己发送的消息,其实这一的效果也是通过ListView来实现的。
这一一个ListView与我们平常所使用的ListView最大不同,就是它拥有两个不同的布局------收到的布局和发送的布局。要实现这一的效果,就需要拿List View的Adapter“开刀了”。
再定义BaseAdapter的时候,需要去重写它的getView方法,这个方法就是用来获取布局的,那么只需要再获取布局的时候,判断下该获取哪一种布局就可以了。而且,ListView再设计的时候已经考虑到了这种清空,所以它提供了两个方法,代码如下所示。
@Override public int getItemViewType(int position) { return super.getItemViewType(position); } @Override public int getViewTypeCount() { return super.getViewTypeCount(); }
getItemViewType()方法用来返回第position个Item是何种类型的,而getViewTypeCount()方法用来返回不同布局的总数。通过这两个方法,再结合getView()方法,就可以很轻松的设计出上面的聊天布局了。
首先来实现两个布局------in和out。布局大小大同小异,只是方向上有区别。固只贴出了一个布局的代码。
<?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" android:layout_marginTop="20dp"> <ImageView android:id="@+id/iv_in" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_in" android:background="@drawable/in_background" android:layout_toRightOf="@id/iv_in" android:gravity="center" android:layout_width="wrap_content" android:layout_height="match_parent" /></RelativeLayout>
同时为了封装下聊天的内容,便于再Adapter中获取数据信息,我们封装了一个Bean来保存聊天信息,代码如下所示。
public class ChatItemBean { private int type; private String info; private Bitmap icon; public ChatItemBean() { } public ChatItemBean(int type, String info, Bitmap icon) { this.type = type; this.info = info; this.icon = icon; } public int getType() { return type; } public String getInfo() { return info; } public Bitmap getIcon() { return icon; } public void setType(int type) { this.type = type; } public void setInfo(String info) { this.info = info; } public void setIcon(Bitmap icon) { this.icon = icon; }}
代码非常简单,我们只是声明了需要的信息并提供了get()和set()方法。
接下来,需要来完成最重要的BaseAdapter了,同样使用ViewHolder模式来提供ListView的效率,并再getView()方法中进行布局类型的判断,从而确定使用哪种布局。
import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;/** * Created by 72312 on 2017/12/21. */public class MyAdapter extends BaseAdapter { private List<ChatItemBean> mList; private Context mContext; public MyAdapter(List<ChatItemBean> mList, Context mContext) { this.mList = mList; this.mContext = mContext; } @Override public int getItemViewType(int position) { return mList.get(position).getType(); } @Override public int getViewTypeCount() { return 2; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder = null; if(convertView == null) { viewHolder = new ViewHolder(); if(getItemViewType(position) == 0){ convertView = LayoutInflater.from(mContext).inflate(R.layout.in,parent,false); viewHolder.iv = convertView.findViewById(R.id.iv_in); viewHolder.tv = convertView.findViewById(R.id.tv_in); }else { convertView = LayoutInflater.from(mContext).inflate(R.layout.out,parent,false); viewHolder.iv = convertView.findViewById(R.id.iv_out); viewHolder.tv = convertView.findViewById(R.id.tv_out); } convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder)convertView.getTag(); } viewHolder.iv.setImageBitmap(mList.get(position).getIcon()); viewHolder.tv.setText(mList.get(position).getInfo()); return convertView; } class ViewHolder { ImageView iv; TextView tv; }}
再以上的代码中,通过再getView中判断getItemViewType(position)的值来决定具体实例化哪个布局,从而实现再一个ListView中多个布局内容的添加。最后,再测试的Activity里面添加一些测试代码,来测试这个布局。
public class MainActivity extends AppCompatActivity { private ListView list_view; private List<ChatItemBean> mList; @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initDataListChat(); list_view = (ListView)findViewById(R.id.list_view); MyAdapter adapter = new MyAdapter(mList,this); list_view.setAdapter(adapter); } private void initDataListChat(){ mList = new ArrayList<>(); for (int i = 0; i < 30; i++) { mList.add(new ChatItemBean(i%2, "hello " + i, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))); } }}
- ListView常用拓展
- ListView常用拓展
- ListView常用拓展
- ListView常用拓展
- ListView常用拓展(Android群英传)
- 《Android群英传》读书笔记(3)第四章:ListView常用拓展
- 可拓展的ListView——ExpandableListView
- Python拓展包:Numpy,pandas...常用函数
- listView拓展的时候出现的拓展页面显示不全解决方法(只显示一条数据)
- Android学习0825<七>(ListView和Adapter拓展)
- [Android] 拓展 BaseAdapter 实现不存储列表项的 ListView
- Android开发09_ExpandableListView可拓展的ListView
- ListView拓展-下拉刷新,顶部图片放大效果
- Android ListView常用用法
- Android ListView常用用法
- AndroidGUI18:ListView常用技巧
- Android ListView常用用法
- listview 常用语句
- 《猎场》有硬伤!独家揭秘硅谷高管的招募日常
- Largest Rectangle in Histogram
- 深度学习(2)——prototxt可视化编译器
- 学习心得3
- 上传图片
- ListView常用拓展
- 12月12日 数据结构 周二
- Coursera deeplearning.ai 深度学习习题2-1-Practical aspects of deep learning
- 各种熵之间的关系
- JavaScript基础(1.介绍)
- annot set the value of read-only property 'outputFile'
- HDOJ1570 A C(打表)
- 第七章--继承与派生
- 2.使用线程实现数字与代码的交替打印