如今,Recyclerview的使用已经深入Android开发者的灵魂,一个布局炫不炫,流不流畅就考验开发人员Recyclerview用得6不6。
先上效果图:
万能适配器CommonAdpater
本adapter来自鸿洋。首先在gradle添加依赖:
compile 'com.zhy:base-rvadapter:3.0.3'
它是一个抽象泛型类,需要写入列表显示的实体类。
用法:
adapter = new CommonAdapter<Bean>(getApplicationContext(), R.layout.item_adapter, datas) {
@Override
protected void convert(ViewHolder holder, Bean bean, int position) {
ImageView imageView = holder.getView(R.id.iv_effectmore_cover);
holder.setText(R.id.tv_effectmore_title, bean.getName());
imageView.setImageResource(bean.getCover());
}
};
参数与普通adapter用法一样,在convert中,要绑定控件和设置数据。直接holder.getView(resId)传入控件id就可以获取控件。
设置textview文字更简单,holder.setText(resId,string)就可以了。
设置点击和长按事件:
adapter.setOnItemClickListener(new MultiItemTypeAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) {
Toast.makeText(getApplicationContext(),"点击第" + position + "个",Toast.LENGTH_SHORT).show();
}
@Override
public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) {
Toast.makeText(getApplicationContext(),"长按第" + position + "个",Toast.LENGTH_SHORT).show();
return false;
}
});
选中居中Recyclerview代码如下:
public class ChooseCenterRecyclerview extends RecyclerView{
private Scroller mScroller;
private int mLastx = 0;
private int mTargetPos;
//用于设置自动平移时候的速度
private float mPxPerMillsec = 0;
public ChooseCenterRecyclerview(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
mScroller = new Scroller(context);
}
@Override
public void computeScroll() {
super.computeScroll();
//computeScrollOffset返回true表示滚动还在继续,持续时间应该就是startScroll设置的时间
if(mScroller!=null && mScroller.computeScrollOffset()){
scrollBy(mLastx - mScroller.getCurrX(), 0);
mLastx = mScroller.getCurrX();
postInvalidate();//让系统继续重绘,则会继续重复执行computeScroll
}
}
//调用此方法滚动到目标位置
public void smoothScrollTo(int fx, int fy,int duration) {
int dx=0;
int dy=0;
if(fx!=0) {
dx = fx - mScroller.getFinalX();
}
if(fy!=0) {
dy = fy - mScroller.getFinalY();
}
smoothScrollBy(dx, dy,duration);
}
//调用此方法设置滚动的相对偏移
public void smoothScrollBy(int dx, int dy,int duration) {
if(duration>0){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy,duration);
}else {
//设置mScroller的滚动偏移量
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
}
/**
* 重绘整个view,重绘过程会调用到computeScroll()方法
*/
invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
}
public void checkAutoAdjust(int position){
int childcount = getChildCount();
//获取可视范围内的选项的头尾位置
int firstvisiableposition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
int lastvisiableposition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
if(position == (firstvisiableposition + 1) || position == firstvisiableposition){
//当前位置需要向右平移
leftScrollBy(position, firstvisiableposition);
}
else if(position == (lastvisiableposition - 1) || position == lastvisiableposition){
//当前位置需要向做平移
rightScrollBy(position, lastvisiableposition);
}
}
private void leftScrollBy(int position, int firstvisiableposition){
View leftChild = getChildAt(0);
if(leftChild != null){
int leftx = leftChild.getLeft();
int startleft = leftx;
int endleft = position == firstvisiableposition?leftChild.getWidth():0;
autoAdjustScroll(startleft, endleft);
}
}
private void rightScrollBy(int position, int lastvisiableposition){
int childcount = getChildCount();
View rightChild = getChildAt(childcount - 1);
if(rightChild != null){
int rightx = rightChild.getRight();
int dx = rightx - getWidth();
int startright = dx;
int endright = position == lastvisiableposition?-1 * rightChild.getWidth():0;
autoAdjustScroll(startright, endright);
}
}
/**
*
* @param start 滑动起始位置
* @param end 滑动结束位置
*/
private void autoAdjustScroll(int start, int end){
int duration = 0;
if(mPxPerMillsec != 0){
duration = (int)((Math.abs(end - start)/mPxPerMillsec));
}
mLastx = start;
if(duration>0) {
mScroller.startScroll(start, 0, end - start, 0, duration);
}else{
mScroller.startScroll(start, 0, end - start, 0);
}
postInvalidate();
}
/**
* 将指定item平滑移动到整个view的中间位置
* @param position
*/
public void smoothToCenter(int position){
int parentWidth = getWidth();//获取父视图的宽度
int childCount = getChildCount();//获取当前视图可见子view的总数
//获取可视范围内的选项的头尾位置
int firstvisiableposition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
int lastvisiableposition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
int count = ((LinearLayoutManager)getLayoutManager()).getItemCount();//获取item总数
mTargetPos = Math.max(0, Math.min(count - 1, position));//获取目标item的位置(参考listview中的smoothScrollToPosition方法)
View targetChild = getChildAt(mTargetPos-firstvisiableposition);//获取目标item在当前可见视图item集合中的位置
View firstChild = getChildAt(0);//当前可见视图集合中的最左view
View lastChild = getChildAt(childCount-1);//当前可见视图集合中的最右view
int childLeftPx = targetChild.getLeft();//子view相对于父view的左边距
int childRightPx = targetChild.getRight();//子view相对于父view的右边距
int childWidth = targetChild.getWidth();
int centerLeft = parentWidth/2-childWidth/2;//计算子view居中后相对于父view的左边距
int centerRight = parentWidth/2+childWidth/2;//计算子view居中后相对于父view的右边距
if(childLeftPx>centerLeft){//子view左边距比居中view大(说明子view靠父view的右边,此时需要把子view向左平移
//平移的起始位置就是子view的左边距,平移的距离就是两者之差
mLastx = childLeftPx;
mScroller.startScroll(childLeftPx,0,centerLeft-childLeftPx,0,600);//600为移动时长,可自行设定
postInvalidate();
}else if(childRightPx<centerRight){
mLastx = childRightPx;
mScroller.startScroll(childRightPx,0,centerRight-childRightPx,0,600);
postInvalidate();
}
}
}
使用方法:
rvCenter.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
if(rvCenter.getChildCount() > 0){
linearLayoutManager.scrollToPositionWithOffset(0,0);
rvCenter.getChildAt(0).requestFocus();
}
}
}
});
adapter.setOnItemClickListener(new MultiItemTypeAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) {
...
rvCenter.smoothToCenter(position);
}
@Override
public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) {
return false;
}
});
关键需要在item点击事件中设置recyclerview.smoothToCenter()
该recyclerview计算了每次手动滚动距离,根据每个item长度和position,计算每次应该到达的位置,从当前位置.scrollby()按照需要滚动距离到达目标位置;
Recyclerview添加Header和Footer
代码如下:
public class HeaderFooterAdapter extends RecyclerView.Adapter<HeaderFooterAdapter.Holder>{
private ArrayList<Bean> mDatas;
private View headerView;
private View footerView;
private int typeItem = 0;
private int typeHeader = 1;
private int typeFooter = 2;
public HeaderFooterAdapter(ArrayList<Bean> datas){
mDatas = datas;
}
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
if(headerView != null && viewType == typeHeader) {
return new Holder(headerView);
}
if(footerView != null && viewType == typeFooter){
return new Holder(footerView);
}
View viewItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_headerfooter,null);
return new Holder(viewItem);
}
@Override
public void onBindViewHolder(Holder holder, int position) {
if(getItemViewType(position) == typeItem){
holder.tvTitle.setText(mDatas.get(position - 1).getName());
}else if(getItemViewType(position) == typeHeader){
}else if(getItemViewType(position) == typeFooter){
}
}
@Override
public int getItemViewType(int position) {
if(headerView == null && footerView == null){
return typeItem;
}
if(position == 0){
return typeHeader;
}
if(position == getItemCount() - 1){
return typeFooter;
}
return typeItem;
}
public void addHeader(View headerView){
this.headerView = headerView;
notifyItemInserted(0);
}
public void addFooter(View footerView){
this.footerView = footerView;
notifyItemInserted(getItemCount()-1);
}
@Override
public int getItemCount() {
int totalCount = mDatas.size();
if(headerView != null) totalCount+=1;
if(footerView != null) totalCount+=1;
return totalCount;
}
public class Holder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView tvTitle;
public Holder(View itemView) {
super(itemView);
if(itemView == headerView){
return;
}
if(itemView == footerView){
return;
}
imageView = itemView.findViewById(R.id.iv_cover);
tvTitle = itemView.findViewById(R.id.tv_title);
}
}
}
用typeHeader,typeFooter,typeItem分别表示布局类型,复写getItemViewType()给当前位置设置类别,在onCreateViewHolder()方法中通过position获取当前itemType来引入不同的布局的Holder。通过设置不同类型type来添加其他布局,采用的是多套布局的思想。不过多套布局是根据实体类的类型来引入不同的layoutId来表示itemtype。多套布局在该系列下一节会讲到。