现在的App的ListView大部分都有分组头部和快速滑动索引,而且分组头部还有挤压的效果,看起来比较炫,于是就在各路大神的博客里找思路,果然发现了几种比较好的实现思路。其中夏神的关于列表快速索引是最好的,我个人认为,git上也有类似的效果但好多是重写ListView实现的,导致可扩展性变得不是很好,夏神是通过自定义View和SectionIndex实现此效果的。而头部分组挤压动画则是郭神的思路最好。通过添加OnScrollListener实现,也没有修改到ListView.
现在:我主要将两位大神的代码合并,调整优化部分代码,顺便改几个小bug。
思路上面基本说清楚了,下面看主要代码实现:
1.快速索引的实现:
/**
* @description 依附于列表的索引View
* @author rzq
* @date 2015年9月19日
*/
public class SideBar extends View
{
/**
* 要绘制的内容
*/
private static String[] biao =
{ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z", "#" };
private Context mContext;
private Paint mPaint;
/**
* View的宽高
*/
private int mWidth, mHeight;
/**
* 单个表的高度
*/
private int mSingleHeight;
private int choose = -1;
/**
*
*/
private boolean isDown;
private OnTouchingLetterListener mChangedListener;
public SideBar(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public SideBar(Context context)
{
this(context, null, 0);
}
public SideBar(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init()
{
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
mPaint.setTextSize(20);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getWidth();
mHeight = getHeight();
mSingleHeight = mHeight / biao.length;
}
/**
* onDraw()尽量值负责与绘制相关的东西
*/
@Override
protected void onDraw(Canvas canvas)
{
setBackgroundDrawable(isDown ? new ColorDrawable(0x99C60000) : new ColorDrawable(0x00000000));
for (int i = 0; i < biao.length; i++)
{
mPaint.setColor(i == choose ? Color.parseColor("#3399ff") : Color.rgb(33, 65, 98));
float xPos = (mWidth - mPaint.measureText(biao[i])) / 2;
float yPos = mSingleHeight * i + mSingleHeight;
canvas.drawText(biao[i], xPos, yPos, mPaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
final int oldChoose = choose;
float y = event.getY();
int c = (int) (y / mHeight * biao.length);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
isDown = true;
if (oldChoose != c)
{
if (c >= 0 && c < biao.length)
{
if (mChangedListener != null)
{
mChangedListener.onTouchingletterChanged(biao[c]);
}
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
isDown = false;
choose = -1;
invalidate();
break;
}
// 将点击事件吃掉
return true;
}
public void setOnTouchingLetterChangedListener(OnTouchingLetterListener onTouchingLetterListener)
{
this.mChangedListener = onTouchingLetterListener;
}
public interface OnTouchingLetterListener
{
public void onTouchingletterChanged(String s);
}
}
package view;
import com.example.sortlistview.R;
import com.example.sortlistview.SortModel;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;
import java.util.List;
/**
* @description 实现了SectionIndexer的可快速滑动Adapter
* @author rzq
* @date 2015年9月19日
*/
public class SortAdapter extends BaseAdapter implements SectionIndexer
{
private List<SortModel> list = null;
private Context mContext;
public SortAdapter(Context mContext, List<SortModel> list)
{
this.mContext = mContext;
this.list = list;
}
@Override
public int getCount()
{
return list.size();
}
@Override
public Object getItem(int position)
{
return list.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder viewHolder = null;
final SortModel mContent = list.get(position);
if (convertView == null)
{
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.item, null);
viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.title);
viewHolder.tvLetter = (TextView) convertView.findViewById(R.id.catalog);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder) convertView.getTag();
}
int section = getSectionForPosition(position);
if (position == getPositionForSection(section))
{
viewHolder.tvLetter.setVisibility(View.VISIBLE);
viewHolder.tvLetter.setText(mContent.getSortLetters());
}
else
{
viewHolder.tvLetter.setVisibility(View.GONE);
}
viewHolder.tvTitle.setText(this.list.get(position).getName());
return convertView;
}
@Override
public Object[] getSections()
{
return null;
}
@Override
public int getPositionForSection(int sectionIndex)
{
for (int i = 0; i < getCount(); i++)
{
String sortStr = list.get(i).getSortLetters();
char firstChar = sortStr.toUpperCase().charAt(0);
if (firstChar == sectionIndex)
{
return i;
}
}
return -1;
}
@Override
public int getSectionForPosition(int position)
{
return list.get(position).getSortLetters().charAt(0);
}
final static class ViewHolder
{
TextView tvLetter;
TextView tvTitle;
}
}
代码基本copy的大神的,对一些代码的位置做了优化。主要是onDraw()让其只负责绘制相关。
2.头部挤压动画实现:
sortListView.setOnScrollListener(new OnScrollListener()
{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
}
/**
* 滑动结束时调用
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
/**
* 获取firstVisibleItem所在section
*/
int section = adapter.getSectionForPosition(firstVisibleItem);
/**
* 获取下一item所在section
*/
int nextSection = adapter.getSectionForPosition(firstVisibleItem + 1);
/**
* 获取下一item所在section的position
*/
int nextSecPosition = adapter.getPositionForSection(nextSection);
/**
* 显示firstVisibleItem所在分组的头部
*/
if (firstVisibleItem != lastFirstVisibleItem)
{
MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();
params.topMargin = 0;
titleLayout.setLayoutParams(params);
if (String.valueOf((char) section) != null)
{
title.setText(String.valueOf((char) section));
}
}
/**
* 说明到了临界位置
*/
if (nextSecPosition == firstVisibleItem + 1)
{
View childView = view.getChildAt(0);
if (childView != null)
{
int titleHeight = titleLayout.getHeight();
int bottom = childView.getBottom();
MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();
/**
* 产生了碰撞,上移titleLayout
*/
if (bottom < titleHeight)
{
float pushedDistance = bottom - titleHeight;
params.topMargin = (int) pushedDistance;
titleLayout.setLayoutParams(params);
}
else
{
if (params.topMargin != 0)
{
params.topMargin = 0;
titleLayout.setLayoutParams(params);
}
}
}
}
lastFirstVisibleItem = firstVisibleItem;
}
});
为ListView添加OnScrollListener,监听ListView的滑动。。。
夏神博客地址:http://blog.csdn.net/xiaanming/article/details/12684155
郭神博客地址:http://blog.csdn.net/guolin_blog/article/details/9033553