ListView全解析
2019-07-07 本文已影响0人
fastcv
前言
ListView作为一个基本过去式的组件,作用就不在这里再说了,直接开始我们的总结。
常用属性
属性 | 说明 |
---|---|
android:listSelector="@color/pink" | listView item 选中时的颜色 |
android:divider="#f9b68b" | 分割线颜色(android:divider="@drawable/@null" 不想显示分割线) |
android:dividerHeight="1dp" | 分割线边距 |
android:scrollbars="none" | 不显示滚动条 |
android:fadingEdge="none" | 去掉上边和下边黑色的阴影 |
android:transcriptMode="alwaysScroll" | 通过设置的控件transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。 |
使用
废话不多说,先来效果图:
listview.gif
简单的使用教程网上有许多,现在我们实现的是自定义的Listview(下拉刷新和上拉加载),适配器使用自定义的万能Listview适配器,分两步进行:
1、自定义Listview
1、继承Listview,重写构造方法
public class RefreshListView extends ListView {
public RefreshListView(Context context) {
super(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2、初始化HeaderView和FooterView
ListView提供两个方法addFooterView和addHeaderView去添加头View和尾部View
private void initFooterView() {
this.setOnScrollListener(this);
mFooterView = View.inflate(getContext(), R.layout.refresh_listview_footer, null);
this.addFooterView(mFooterView);
tv_pull_list_header_title = mFooterView.findViewById(R.id.tv_pull_list_header_title);
pb_pull_list_header = mFooterView.findViewById(R.id.pb_pull_list_header);
mFooterView.measure(0,0);
mFooterViewHeight = mFooterView.getMeasuredHeight(); //得到FooterView的高度
mFooterView.setPadding(0,0,0,0-mFooterViewHeight); //隐藏FooterView
}
private void initHeaderView() {
mHearder = View.inflate(getContext(), R.layout.refresh_listview_header, null);
this.addHeaderView(mHearder);
refresh_tips = mHearder.findViewById(R.id.refresh_tips);
refresh_last_time = mHearder.findViewById(R.id.refresh_last_time);
ivArrow = mHearder.findViewById(R.id.ivArrow);
pbWaiting = mHearder.findViewById(R.id.pbWaiting);
mHearder.measure(0,0);
mHeaderHeight = mHearder.getMeasuredHeight(); //得到HeaderView的高度
mHearder.setPadding(0, 0-mHeaderHeight,0,0); //隐藏HeaderView
}
3、重写onTouchEvent方法监听HeaderView的状态
这里的逻辑是,拿到点击时的y坐标,在移动的时候去得到偏移量,然后和我们我HeaderView的高度进行运算得到padding,这个值即为我们HeaderView需要隐藏的高度
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
{
if (valueAnimator.isRunning())
valueAnimator.end();
startY = (int) ev.getY();
Log.e("SayHallo","MotionEvent.ACTION_DOWN startY = " + startY);
}
break;
case MotionEvent.ACTION_MOVE:
{
Log.e("SayHallo","MotionEvent.ACTION_MOVE");
if (startY == -1) {// 确保startY有效
startY = (int) ev.getY();
}
if(mCurrrentState == LoadState.STATE_REFRESHING) break;
endY = (int) ev.getY();
moveY = px2dp(endY - startY,getContext());
if( moveY > 0 && getFirstVisiblePosition() == 0){
// 计算padding
padding = moveY-mHeaderHeight;
if(padding < 1) //预留位置
mHearder.setPadding(0,padding,0,0);
if (padding > 0 && mCurrrentState != LoadState.STATE_RELEASE_REFRESH )
{
mCurrrentState = LoadState.STATE_RELEASE_REFRESH;
refreshState();
}else if (padding < 0 && mCurrrentState != LoadState.STATE_PULL_REFRESH) {// 改为下拉刷新状态
mCurrrentState = LoadState.STATE_PULL_REFRESH;
refreshState();
}
return true;
}
}
break;
case MotionEvent.ACTION_UP:
{
Log.e("SayHallo","padding = " + padding);
if (mCurrrentState == LoadState.STATE_RELEASE_REFRESH)
{
mCurrrentState = LoadState.STATE_REFRESHING;
mHearder.setPadding(0,0,0,0);
refreshState();
}else if (mCurrrentState == LoadState.STATE_PULL_REFRESH){
if (padding != 0)
scroll_Start(padding);
}
startY = -1; //重置
padding = 0;
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
4、实现onScrollStateChanged方法监听FooterView的状态
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.e("SayHallo","到底了.....");
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
|| scrollState == OnScrollListener.SCROLL_STATE_FLING) {
if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {// 滑动到最后
Log.e("SayHallo","到底了.....");
// mFooterView.setPadding(0, 0, 0, 0);// 显示
setSelection(getCount() - 1);// 改变listview显示位置
isLoadingMore = true;
if (mListener != null) {
mListener.onLoadMore();
}
}
}
}
完整的代码如下:
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
//头View中的一些组件
private View mHearder;
private TextView refresh_tips; //显示文字(刷新状态)
private TextView refresh_last_time; //显示时间(刷新完成)
private ImageView ivArrow; //指向图
private ProgressBar pbWaiting; //刷新的圈圈
private int mHeaderHeight; //头View的测量高度
//脚View中的一些组件
private View mFooterView;
private TextView tv_pull_list_header_title; //显示文字(加载状态)
private ProgressBar pb_pull_list_header; //刷新的圈圈
private int mFooterViewHeight; //脚View的测量高度
private int startY = -1; //按下时的Y坐标(相对于屏幕的绝对坐标)
private LoadState mCurrrentState = LoadState.STATE_PULL_REFRESH;
private int endY;
private int moveY;
private ValueAnimator valueAnimator;
private int padding = 0;
private int lastEndY = 0;
public RefreshListView(Context context) {
super(context);
initHeaderView();
initFooterView();
initArrowAnim();
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeaderView();
initFooterView();
initArrowAnim();
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
initArrowAnim();
}
private void initFooterView() {
this.setOnScrollListener(this);
mFooterView = View.inflate(getContext(), R.layout.refresh_listview_footer, null);
this.addFooterView(mFooterView);
tv_pull_list_header_title = mFooterView.findViewById(R.id.tv_pull_list_header_title);
pb_pull_list_header = mFooterView.findViewById(R.id.pb_pull_list_header);
mFooterView.measure(0,0);
mFooterViewHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0,0,0,0-mFooterViewHeight);
}
private void initHeaderView() {
mHearder = View.inflate(getContext(), R.layout.refresh_listview_header, null);
this.addHeaderView(mHearder);
refresh_tips = mHearder.findViewById(R.id.refresh_tips);
refresh_last_time = mHearder.findViewById(R.id.refresh_last_time);
ivArrow = mHearder.findViewById(R.id.ivArrow);
pbWaiting = mHearder.findViewById(R.id.pbWaiting);
mHearder.measure(0,0);
mHeaderHeight = mHearder.getMeasuredHeight();
mHearder.setPadding(0, 0-mHeaderHeight,0,0);
}
private void initArrowAnim() {
valueAnimator = ValueAnimator.ofInt(0, 0);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
{
if (valueAnimator.isRunning())
valueAnimator.end();
startY = (int) ev.getY();
Log.e("SayHallo","MotionEvent.ACTION_DOWN startY = " + startY);
}
break;
case MotionEvent.ACTION_MOVE:
{
Log.e("SayHallo","MotionEvent.ACTION_MOVE");
if (startY == -1) {// 确保startY有效
startY = (int) ev.getY();
}
if(mCurrrentState == LoadState.STATE_REFRESHING) break;
endY = (int) ev.getY();
moveY = px2dp(endY - startY,getContext());
if( moveY > 0 && getFirstVisiblePosition() == 0){
// 计算padding
padding = moveY-mHeaderHeight;
if(padding < 1) //预留位置
mHearder.setPadding(0,padding,0,0);
if (padding > 0 && mCurrrentState != LoadState.STATE_RELEASE_REFRESH )
{
mCurrrentState = LoadState.STATE_RELEASE_REFRESH;
refreshState();
}else if (padding < 0 && mCurrrentState != LoadState.STATE_PULL_REFRESH) {// 改为下拉刷新状态
mCurrrentState = LoadState.STATE_PULL_REFRESH;
refreshState();
}
return true;
}
}
break;
case MotionEvent.ACTION_UP:
{
Log.e("SayHallo","padding = " + padding);
if (mCurrrentState == LoadState.STATE_RELEASE_REFRESH)
{
mCurrrentState = LoadState.STATE_REFRESHING;
mHearder.setPadding(0,0,0,0);
refreshState();
}else if (mCurrrentState == LoadState.STATE_PULL_REFRESH){
if (padding != 0)
scroll_Start(padding);
}
startY = -1; //重置
padding = 0;
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
//px转化为dp
public static int px2dp(float pxValue, Context activity) {
final float scale = activity.getResources().getDisplayMetrics().density;
return (int) ((pxValue - 0.5f) / scale);
}
/**
* 刷新下拉控件的布局
*/
private void refreshState() {
switch (mCurrrentState) {
case STATE_PULL_REFRESH:
refresh_tips.setText("下拉刷新");
ivArrow.setVisibility(View.VISIBLE);
pbWaiting.setVisibility(View.INVISIBLE);
// ivArrow.startAnimation(animDown);
break;
case STATE_RELEASE_REFRESH:
refresh_tips.setText("松开刷新");
ivArrow.setVisibility(View.VISIBLE);
pbWaiting.setVisibility(View.INVISIBLE);
// ivArrow.startAnimation(animUp);
break;
case STATE_REFRESHING:
refresh_tips.setText("正在刷新...");
ivArrow.clearAnimation();// 必须先清除动画,才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
pbWaiting.setVisibility(View.VISIBLE);
if (mListener != null) {
mListener.onRefresh();
}
break;
default:
break;
}
}
OnRefreshListener mListener;
private boolean isLoadingMore;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.e("SayHallo","到底了.....");
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
|| scrollState == OnScrollListener.SCROLL_STATE_FLING) {
if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {// 滑动到最后
Log.e("SayHallo","到底了.....");
// mFooterView.setPadding(0, 0, 0, 0);// 显示
setSelection(getCount() - 1);// 改变listview显示位置
isLoadingMore = true;
if (mListener != null) {
mListener.onLoadMore();
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
public interface OnRefreshListener {
void onRefresh();
void onLoadMore();// 加载下一页数据
}
public void setOnRefreshListener(OnRefreshListener listener) {
mListener = listener;
}
/*
* 收起下拉刷新的控件
*/
public void onRefreshComplete(boolean success) {
if (isLoadingMore) {// 正在加载更多...
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏脚布局
isLoadingMore = false;
} else {
mCurrrentState = LoadState.STATE_PULL_REFRESH;
ivArrow.setVisibility(View.VISIBLE);
pbWaiting.setVisibility(View.INVISIBLE);
scroll_Start(1);
// mHearder.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏
if (success) {
refresh_tips.setText("刷新成功");
refresh_last_time.setText("最后刷新时间:" + getCurrentTime());
}
}
}
/**
* 获取当前时间
*/
public String getCurrentTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(new Date());
}
private void scroll_Start(int p) {
valueAnimator.setIntValues(p,-mHeaderHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mHearder.setPadding(0,value,0,0);
}
});
valueAnimator.setDuration(500);
valueAnimator.start();
}
enum LoadState {
STATE_PULL_REFRESH, // 下拉刷新
STATE_RELEASE_REFRESH , // 松开刷新
STATE_REFRESHING //正在刷新
}
}
自定义万能Listview适配器
1、首先,实现万能ViewHolder
public class ListViewHolder {
private SparseArray<View> mViews;
private int mPosition;
private View mConvertView;
private Context context;
public ListViewHolder(Context context, ViewGroup parent, int position, int layoutId) {
this.mPosition = position;
this.context = context;
this.mViews = new SparseArray<View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mConvertView.setTag(this);
}
public static ListViewHolder get(Context context, View convertView,
ViewGroup parent,int position, int layoutId) {
if (convertView == null) {
return new ListViewHolder(context, parent, position, layoutId);
} else {
ListViewHolder holder = (ListViewHolder) convertView.getTag();
return holder;
}
}
/**
* 通过viewId获取控件
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getConvertView() {
return mConvertView;
}
/**
* 封装setText方法,设置TextView的值
*/
public ListViewHolder setTextViewVualue(int viewId, String text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
* 封装setText方法,设置TextView的值
*/
public ListViewHolder setImageViewVualue(int viewId, int id) {
ImageView iv = getView(viewId);
iv.setImageDrawable(context.getResources().getDrawable(id));
return this;
}
}
2、实现万能适配器
public abstract class AbsListViewAdapter<T> extends BaseAdapter {
protected Context mContext;
protected List<T> mDatas;
protected LayoutInflater mInflater;
protected int mLayoutId;
public AbsListViewAdapter(Context context, List<T> datas, int LayoutId) {
this.mContext = context;
mInflater = LayoutInflater.from(context);
this.mLayoutId = LayoutId;
this.mDatas = datas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public T getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewHolder holder = ListViewHolder.get(mContext, convertView,
parent, position, mLayoutId);
convert(holder, getItem(position));
return holder.getConvertView();
}
public abstract void convert(ListViewHolder holder, T t);
}
实现Listview的显示
1、实现实体类
public class Enety{
public Enety(String title, int icon) {
this.title = title;
this.icon = icon;
}
public String title;
public int icon;
}
2、实现适配器
class MyAdapter extends AbsListViewAdapter<Enety> {
public MyAdapter(Context context, List<Enety> datas, int LayoutId) {
super(context, datas, LayoutId);
}
@Override
public void convert(ListViewHolder holder, Enety enety) {
holder.setTextViewVualue(R.id.title, enety.title)
.setImageViewVualue(R.id.image, enety.icon);
}
}
3、实现整个效果
public class MainActivity extends AppCompatActivity {
private RefreshListView listView;
private MyAdapter adapter1;
private ArrayList<Enety> eneties;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.refreshlist);
eneties = new ArrayList<>();
for (int i = 0;i< 100 ; i++) {
Enety enety = new Enety("这是标题"+i,R.mipmap.ic_launcher);
eneties.add(enety);
}
adapter1 = new MyAdapter(this,eneties,R.layout.shouxinrenzhengliebiao);
listView.setAdapter(adapter1);
listView.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Enety enety = new Enety("下拉新增数据",R.mipmap.ic_launcher);
eneties.add(0,enety);
adapter1.notifyDataSetChanged();
listView.onRefreshComplete(true);
}
},3000);
}
@Override
public void onLoadMore() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Enety enety = new Enety("上拉加载数据",R.mipmap.ic_launcher);
eneties.add(enety);
adapter1.notifyDataSetChanged();
listView.onRefreshComplete(true);
}
},500);
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.e("Say1","position = " + position);
}
});
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Log.e("Say1","onItemLongClick position = " + position);
return true;
}
});
}
public class Enety{
public Enety(String title, int icon) {
this.title = title;
this.icon = icon;
}
public String title;
public int icon;
}
class MyAdapter extends AbsListViewAdapter<Enety> {
public MyAdapter(Context context, List<Enety> datas, int LayoutId) {
super(context, datas, LayoutId);
}
@Override
public void convert(ListViewHolder holder, Enety enety) {
holder.setTextViewVualue(R.id.title, enety.title)
.setImageViewVualue(R.id.image, enety.icon);
}
}
}