Material Design之RecyclerView基本讲解与瀑布流的实现

RecyclerView简介

  RecyclerView是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式。它被作为ListView和GridView控件的继承者,具有更优的灵活性与可替代性。在最新的support-v7版本中提供支持。本文将讲解RecyclerView的简单实现,添加删除条目,点击事件添加与瀑布流的实现。

相关原理与简单实现

添加依赖

  在AndroidStudio的build.gradle中添加依赖:

dependencies {
    ...
    compile 'com.android.support:recyclerview-v7:25.3.0'
}

在布局中使用

  添加完依赖后就可以在布局中使用RecyclerView了:

<android.support.v7.widget.RecyclerView
    android:id="@+id/main_recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

RecyclerView.Adapter

  RecyclerView封装了一种新型的适配器,与现在使用的适配器大同小异。它强制用户使用RecyclerView提供的ViewHolder,使用时主要需要重写onCreateViewHolder与onBindViewHolder方法。前者用来展现视图及其持有者,且只有真正需要一个新view时才会被回调,不需要检查是否已经被回收。后者用来绑定数据到View上。

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private Context context;
    private List<String> list;
    private LayoutInflater inflater;

    public MyAdapter(Context context, List<String> list) {
        this.context = context;
        this.list = list;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.item_recyclerview, parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textView.setText(list.get(position));
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        public MyViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.item_textview);
        }
    }
}

LayoutManager

  RecyclerView通过布局管理器LayoutManager控制每一个item如何进行排列摆放,何时展示和隐藏。回收或重用一个View时LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制避免了创建过多的View和频繁的调用findViewById方法,目前其自带的主要有以下三种:

  • LinearLayoutManager:ListView样式
  • GridLayoutManager:GridView样式
  • StaggeredGridLayoutManager:瀑布流样式

ItemDecoration

  RecyclerView并不能像ListView一样直接在xml布局中修改item分割线样式。需要在Activity动态设置,当然更推荐在单个条目布局中设置margin或者padding来实现分割线效果,这里提供一个分割线。

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private Drawable mDivider;
    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
            }
    }
}

ItemAnimator

  ItemAnimator会根据适配器上收到的通知来动画显示视图组的修改,比如item的添加与删除。DefaultItemAnimator已经能很好的展现动画效果了。

RecyclerView初始化

  若想初始化一个RecyclerView使其进入工作状态,你需要在Activity中做以下的操作:

RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_main);
MyAdapter adapter = new MyAdapter(this, list);
//设置RecyclerView保持固定的大小
recyclerView.setHasFixedSize(true);
//设置适配器
recyclerView.setAdapter(adapter);
//设置RecyclerView ListView样式布局管理
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//设置RecyclerView的Item分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
//设置RecyclerView的动画
recyclerView.setItemAnimator(new DefaultItemAnimator());

//设置RecyclerView GridView样式
//recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this, 3));
//设置RecyclerView 水平GridView样式
//recyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));
//设置RecyclerView 瀑布流样式
//recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

点击事件与添加删除Item

  美中不足的是RecyclerView并没有提供像ListView一样的Item点击与Item长点击事件,不提供咱们就自己造,通过接口回调来实现。

Adapter中

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private Context context;
    private LayoutInflater inflater;
    protected ArrayList<String> datas;
    private onItemClickedListener onItemClickedListener;

    public MyAdapter(Context context, ArrayList<String> datas) {
        this.context = context;
        this.datas = datas;
        inflater = LayoutInflater.from(context);
    }

    public void setOnItemClickedListener(MyAdapter.onItemClickedListener onItemClickedListener) {
        this.onItemClickedListener = onItemClickedListener;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textView.setText(datas.get(position));
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public int getItemCount() {
        return datas.size();
    }

   /**
    * 添加条目
    */
    public void addItem(int position) {
        datas.add(position, "xulei");
    //        notifyDataSetChanged();
        notifyItemInserted(position);//调用这个才有动画效果
    }

   /**
    * 移除条目
    */
    public void removeItem(int position) {
        datas.remove(position);
        notifyItemRemoved(position);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public MyViewHolder(View itemView) {
            super(itemView);
            //初始化控件
            textView = (TextView) itemView.findViewById(R.id.item_textview);
            //设置当前条目单击监听
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (onItemClickedListener != null)
                        onItemClickedListener.onClick(view, getAdapterPosition());//可立刻获取到当前position
//                        onItemClickedListener.onClick(view, getLayoutPosition());//需等当前视图更新完才能获取到当前position,<16ms。
                }
            });
            //设置当前条目长按监听
            itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    if (onItemClickedListener != null)
                        onItemClickedListener.onLongClick(view, getAdapterPosition());
                    return false;
                }
            });
        }
    }

   /**
    * 点击回调的接口
    */
    interface onItemClickedListener {
        void onClick(View view, int position);

        void onLongClick(View view, int position);
    }
}

Activity中

  在Activity中实例化Adapter之后添加如下代码:

adapter.setOnItemClickedListener(new MyAdapter.onItemClickedListener() {
    @Override
    public void onClick(View view, int position) {
        adapter.addItem(position);
        Toast.makeText(MainActivity.this, "点击click:" + position, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLongClick(View view, int position) {
        adapter.removeItem(position);
        Toast.makeText(MainActivity.this, "长按click:" + position, Toast.LENGTH_SHORT).show();
    }
});

瀑布流的实现

  想实现瀑布流的样式通过使用RecyclerView也很容易就能实现。

首先在Activity中设置LayoutManager时选择:

recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

修改Adapter

  瀑布流自然是每个条目的高度不同才能出现瀑布的效果(水平布局则是宽度不同),那么只需在每个条目绑定数据时动态改变下其高度即可,贴出瀑布流Adapter代码:

import android.content.Context;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class StaggerAdapter extends MyAdapter {
    private List<Integer> heights;
    public StaggerAdapter(Context context, ArrayList<String> datas) {
        super(context, datas);
        heights = new ArrayList<>();
        for (int i = 0; i < datas.size(); i++) {
            heights.add((int) (100 + Math.random() * 300));
        }
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        layoutParams.height = heights.get(position);
//        layoutParams.width = heights.get(position);
        holder.itemView.setLayoutParams(layoutParams);
        holder.textView.setText(datas.get(position));
    }
}

总结

  通过实践发现RecyclerView相较于ListView与GridView确实强大很多,更加的灵活与方便,提高了开发效率。但也有不足之处,如并未封装点击事件的回调,确实是比较头疼。期待Google的完善。
附上GitHub源码:
RecyclerViewDemo
RecyclerViewDevelop

展开阅读全文

UltimateRecyclerView 添加头部view无效?

11-24
这是fragmetn @EFragment(R.layout.fragment_home) public class HomeFragment extends BaseFragment<AppApplication> { protected LoadingDialog loading; @ViewById UltimateRecyclerView home_ultimateRecyclerView; protected GroupGoods groupGoodses; protected HomeRecyclerViewAdapter homeRecyclerViewAdapter ; View headerView; protected int [] groupIco=new int[]{R.mipmap.shape, R.mipmap.previewflaticons, R.mipmap.office_supplies, R.mipmap.pneumatic_hydraulic, R.mipmap.mechanics, R.mipmap.protect, R.mipmap.industrial_contro, R.mipmap.light}; private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private String mParam1; private String mParam2; public HomeFragment() { } public static HomeFragment newInstance(String param1, String param2) { HomeFragment fragment = new HomeFragment(); Bundle args = new Bundle(); args.putString(ARG_PARAM1, param1); args.putString(ARG_PARAM2, param2); fragment.setArguments(args); return fragment; } /** * 请求首页商品 */ public void getDataGroupGoods(){ VolleyUtils .getInstance(getActivity()).doStringRequest(Url.IndexFloors, new Response.Listener<String>() { @Override public void onResponse(String response) { groupGoodses=GsonUtils.GsonToBean(response,GroupGoods.class); homeRecyclerViewAdapter.setData(groupGoodses); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { headerView=LayoutInflater.from(getActivity()).inflate(R.layout.header_view,null); return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } getDataGroupGoods(); } @AfterViews public void initUltimaterecyclerview() { homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(getContext(), groupGoodses, groupIco); home_ultimateRecyclerView.setHasFixedSize(false); //设置布局管理器 home_ultimateRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false)); home_ultimateRecyclerView.setAdapter(homeRecyclerViewAdapter); //设置点击事件 homeRecyclerViewAdapter.setOnclickListeners(new HomeRecyclerViewAdapter.OnclickListeners() { @Override public void setOnClick(String id, int type) { switch (type) { case HomeRecyclerViewAdapter.GOODS_TYPE: Toast.makeText(getContext(), "商品" + id, Toast.LENGTH_LONG).show(); break; case HomeRecyclerViewAdapter.GROUP_TYPE: Toast.makeText(getContext(), "组" + id, Toast.LENGTH_LONG).show(); break; } } }); home_ultimateRecyclerView.setParallaxHeader(headerView); } } 这是适配器adapter /** * Created by arvin on 2016/11/18. */ public class HomeRecyclerViewAdapter extends UltimateViewAdapter<HomeRecyclerViewAdapter.HomeRecyclerViewHolder> { public static final int GROUP_TYPE=0; public static final int GOODS_TYPE=1; private Context context ; //首页分类含商品 private GroupGoods groupGoods; //分类图标 private int [] groupIco ; private OnclickListeners onclickListeners; public HomeRecyclerViewAdapter( Context context, GroupGoods groupGoods,int [] groupIco ) { this.context=context; this.groupGoods = groupGoods; this.groupIco=groupIco; } public void setData(GroupGoods groupGoods) { this.groupGoods = groupGoods; this.notifyDataSetChanged(); } @Override public HomeRecyclerViewHolder newFooterHolder(View view) { return new HomeRecyclerViewHolder(view); } @Override public HomeRecyclerViewHolder newHeaderHolder(View view) { return new HomeRecyclerViewHolder(view); } @Override public HomeRecyclerViewHolder onCreateViewHolder(ViewGroup parent) { View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.home_recyclerview_item,parent,false); return new HomeRecyclerViewHolder(view); } @Override public int getAdapterItemCount() { if(groupGoods==null) { return 0; }else {return groupGoods.getData().size(); } } @Override public long generateHeaderId(int position) { return 0; } /*** * 绑定数据 * @param holder * @param position */ @Override public void onBindViewHolder(HomeRecyclerViewHolder holder, int position) { // if (position < getItemCount() && (customHeaderView != null ? position <= groupGoods.getData().size() : position < groupGoods.getData().size()) && (customHeaderView != null ? position > 0 : true)) { // position -= customHeaderView==null?0:1; final GroupGoods.Group group = groupGoods.getData().get(position); //设置首页分类图标 holder.group_name.setText(group.getName()); //设置分类图标 Drawable drawableleft =context.getResources().getDrawable(groupIco[position]); /// 这一步必须要做,否则不会显示. drawableleft.setBounds(0, 0, drawableleft.getMinimumWidth(), drawableleft.getMinimumHeight()); holder.group_name.setCompoundDrawables(drawableleft, null, null, null); holder.goodsName1.setText(group.getGoods().get(0).getCaption()); holder.goodsName2.setText(group.getGoods().get(1).getCaption()); GlideImgManager.loadImage(context,group.getGoods().get(0).getPic(),holder.goodsImageView1); GlideImgManager.loadImage(context,group.getGoods().get(1).getPic(),holder.goodsImageView2); holder.price1.setText(group.getGoods().get(0).getPrice()); holder.price2.setText(group.getGoods().get(1).getPrice()); if(onclickListeners!=null) { holder.group_layout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onclickListeners.setOnClick(group.getId(),GROUP_TYPE); } }); holder.goods_name1_RelativeLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onclickListeners.setOnClick(group.getGoods().get(0).getId()+"",GOODS_TYPE); } }); holder.goods_name2_RelativeLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onclickListeners.setOnClick(group.getGoods().get(1).getId()+"",GOODS_TYPE); } }); } } // } @Override public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_view,parent,false); return new HomeRecyclerViewHolder(view); } @Override public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder, int position) { if(customHeaderView!=null){ position-=1; } } public void setOnclickListeners(OnclickListeners onclickListeners) { this.onclickListeners=onclickListeners; } class HomeRecyclerViewHolder extends UltimateRecyclerviewViewHolder { TextView price2;........ TextView goodsName1; public HomeRecyclerViewHolder(View itemView) { super(itemView); price2= (TextView) itemView.findViewById(R.id.price2); } } public interface OnclickListeners { void setOnClick(String id,int type); } } 大神们来帮我看看!
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值