前面的文章已经介绍横向ListView的基础实现、快速滑动和事件响应实现;可以说,通过前面两篇文章已经实现了一个完整可用的横向ListView控件,而这以后的文章将介绍的是整个控件的扩展功能,以满足日常开发过程中的特殊需求
本文将介绍列表头/尾的添加功能实现以及整个视图在没有足够item可以铺满控件时,让显示内容剧中显示。
为什么要实现添加头尾视图,这个我个人也不是很清楚,毕竟在开发过程中很少会有使用头尾视图的需要;不过为了学习,还为了以后也许可能有这方面的需求,所以还是选择了实现这个功能;对于内容剧中显示功能,是因为在使用这个控件时刚好有这个需求。
有一点值得注意:头/尾视图在设计和使用的概念上不是作为列表中的item,如果这一点没有弄清楚,那么在阅读源代码时会较为困难,其中八个概念性封装的方法就尤为体现出这一点(八个方法具体见代码)
先上代码:
package com.hss.os.horizontallistview.history_version;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;
import java.util.LinkedList;
import java.util.Queue;
/**
* 添加头/尾视图及居中显示
* Created by sxyx on 2017/8/8.
*/
public class HorizontalListView3 extends AdapterView<ListAdapter> {
private Queue<View> cacheView = new LinkedList<>();//列表项缓存视图
private ListAdapter adapter = null;
private GestureDetector mGesture;
private int firstItemIndex = 0;//显示的第一个子项的下标
private int lastItemIndex = -1;//显示的最后的一个子项的下标
private int scrollValue=0;//列表已经发生有效滚动的位移值
private int hasToScrollValue=0;//接下来列表发生滚动所要达到的位移值
private int maxScrollValue=Integer.MAX_VALUE;//列表发生滚动所能达到的最大位移值(这个由最后显示的列表项决定)
private int displayOffset=0;//列表显示的偏移值(用于矫正列表显示的所有子项的显示位置)
private Scroller mScroller;
private int firstItemLeftEdge=0;//第一个子项的左边界
private int lastItemRightEdge=0;//最后一个子项的右边界
private View headView;
private View footView;
private boolean hasHeadView=false;
private boolean hasFootView=false;
private boolean canShowInMid=false;
public HorizontalListView3(Context context) {
super(context);
init(context);
}
public HorizontalListView3(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public HorizontalListView3(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public HorizontalListView3(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context){
mGesture = new GestureDetector(getContext(), mOnGesture);
mScroller=new Scroller(context);
}
private void initParams(){
mScroller.forceFinished(true);//避免在滑动过程中变换视图内容时,出现列表无法滚动的情况
removeAllViewsInLayout();
if(adapter!=null&&lastItemIndex<adapter.getCount())
hasToScrollValue=scrollValue;//保持显示位置不变
else hasToScrollValue=0;//滚动到列表头
scrollValue=0;//列表已经发生有效滚动的位移值
firstItemIndex = 0;//显示的第一个子项的下标
lastItemIndex = -1;//显示的最后的一个子项的下标
maxScrollValue=Integer.MAX_VALUE;//列表发生滚动所能达到的最大位移值(这个由最后显示的列表项决定)
displayOffset=0;//列表显示的偏移值(用于矫正列表显示的所有子项的显示位置)
firstItemLeftEdge=0;//第一个子项的左边界
lastItemRightEdge=0;//最后一个子项的右边界
if(hasHeadView||hasFootView) {
if (hasHeadView) {
scrollValue = headView.getMeasuredWidth();
headView.layout(0, 0, 0, 0);
setHeadView(headView);
}
if (hasFootView) {
footView.layout(0, 0, 0, 0);
setFootView(footView);
}
}else requestLayout();
}
private DataSetObserver mDataObserver = new DataSetObserver() {
@Override
public void onChanged() {
//执行Adapter数据改变时的逻辑
initParams();
}
@Override
public void onInvalidated() {
//执行Adapter数据失效时的逻辑
initParams();
}
};
@Override
public ListAdapter getAdapter() {
return adapter;
}
@Override
public void setAdapter(ListAdapter adapter) {
if(adapter!=null){
adapter.registerDataSetObserver(mDataObserver);
}
if(this.adapter!=null){
this.adapter.unregisterDataSetObserver(mDataObserver);
}
this.adapter=adapter;
requestLayout();
}
@Override
public View getSelectedView() {
return null;
}
@Override
public void setSelection(int i) {
}
private void addAndMeasureChild(View child, int viewIndex) {
LayoutParams params = child.getLayout