精华内容
下载资源
问答
  • RecyclerView

    2015-12-20 21:56:55
    Android RecyclerView part 1 blog -->RecyclerView使用详解(一) Android RecyclerView part 2 blog -->RecyclerView使用详解(二) Android RecyclerView part 3 blog -->RecyclerView使用详解(三)

    Android RecyclerView part 1 blog -->RecyclerView使用详解(一)

    Android RecyclerView part 2 blog -->RecyclerView使用详解(二)

    Android RecyclerView part 3 blog -->RecyclerView使用详解(三)

    展开全文
  • Android RecyclerView 使用完全解析 体验艺术般的控件

    万次阅读 多人点赞 2015-04-16 09:07:16
    Android RecyclerView 使用完全解析 概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实...

    转载请标明出处:
    http://blog.csdn.net/lmj623565791/article/details/45059587
    本文出自:【张鸿洋的博客】

    概述

    RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。
    据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。

    那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。

    • 你想要控制其显示的方式,请通过布局管理器LayoutManager
    • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
    • 你想要控制Item增删的动画,请通过ItemAnimator
    • 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)

    基本使用

    鉴于我们对于ListView的使用特别的熟悉,对比下RecyclerView的使用代码:

    mRecyclerView = findView(R.id.id_recyclerview);
    //设置布局管理器
    mRecyclerView.setLayoutManager(layout);
    //设置adapter
    mRecyclerView.setAdapter(adapter)
    //设置Item增加、移除动画
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    //添加分割线
    mRecyclerView.addItemDecoration(new DividerItemDecoration(
                    getActivity(), DividerItemDecoration.HORIZONTAL_LIST));

    ok,相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?

    那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。

    Just like ListView

    • Activity
    package com.zhy.sample.demo_recyclerview;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.RecyclerView.ViewHolder;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    public class HomeActivity extends ActionBarActivity
    {
    
        private RecyclerView mRecyclerView;
        private List<String> mDatas;
        private HomeAdapter mAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_single_recyclerview);
    
            initData();
            mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
            mRecyclerView.setAdapter(mAdapter = new HomeAdapter());
    
        }
    
        protected void initData()
        {
            mDatas = new ArrayList<String>();
            for (int i = 'A'; i < 'z'; i++)
            {
                mDatas.add("" + (char) i);
            }
        }
    
        class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
        {
    
            @Override
            public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
            {
                MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
                        HomeActivity.this).inflate(R.layout.item_home, parent,
                        false));
                return holder;
            }
    
            @Override
            public void onBindViewHolder(MyViewHolder holder, int position)
            {
                holder.tv.setText(mDatas.get(position));
            }
    
            @Override
            public int getItemCount()
            {
                return mDatas.size();
            }
    
            class MyViewHolder extends ViewHolder
            {
    
                TextView tv;
    
                public MyViewHolder(View view)
                {
                    super(view);
                    tv = (TextView) view.findViewById(R.id.id_num);
                }
            }
        }
    
    }
    • Activity的布局文件
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/id_recyclerview"
             android:divider="#ffff0000"
               android:dividerHeight="10dp"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </RelativeLayout>
    • Item的布局文件
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:background="#44ff0000"
        android:layout_height="wrap_content" >
    
        <TextView
            android:id="@+id/id_num"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:text="1" />
    </FrameLayout>

    这么看起来用法与ListView的代码基本一致哈~~
    看下效果图:

    20150415145840351

    看起来好丑,Item间应该有个分割线,当你去找时,你会发现RecyclerView并没有支持divider这样的属性。那么怎么办,你可以给Item的布局去设置margin,当然了这种方式不够优雅,我们文章开始说了,我们可以自由的去定制它,当然我们的分割线也是可以定制的。

    ItemDecoration

    我们可以通过该方法添加分割线:
    mRecyclerView.addItemDecoration()
    该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类(我觉得最好能提供几个)。
    该类的源码:

    public static abstract class ItemDecoration {
    
    public void onDraw(Canvas c, RecyclerView parent, State state) {
                onDraw(c, parent);
     }
    
    
    public void onDrawOver(Canvas c, RecyclerView parent, State state) {
                onDrawOver(c, parent);
     }
    
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
                getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
                        parent);
    }
    
    @Deprecated
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
                outRect.set(0, 0, 0, 0);
     }
    

    当我们调用mRecyclerView.addItemDecoration()方法添加decoration的时候,RecyclerView在绘制的时候,去会绘制decorator,即调用该类的onDraw和onDrawOver方法,

    • onDraw方法先于drawChildren
    • onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。
    • getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。

    接下来我们看一个RecyclerView.ItemDecoration的实现类,该类很好的实现了RecyclerView添加分割线(当使用LayoutManager为LinearLayoutManager时)。
    该类参考自:DividerItemDecoration

    
    package com.zhy.sample.demo_recyclerview;
    
    /*
     * Copyright (C) 2014 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * limitations under the License.
     */
    
    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.support.v7.widget.RecyclerView.State;
    import android.util.Log;
    import android.view.View;
    
    
    /**
     * This class is from the v7 samples of the Android SDK. It's not by me!
     * <p/>
     * See the license above for details.
     */
    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) {
            Log.v("recyclerview - itemdecoration", "onDraw()");
    
            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);
                android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
                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);
            }
        }
    }

    该实现类可以看到通过读取系统主题中的 android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。如果你不清楚它是怎么做到的读取系统的属性用于自身,请参考我的另一篇博文:Android 深入理解Android中的自定义属性

    获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect去设置了绘制的范围。onDraw中实现了真正的绘制。

    我们在原来的代码中添加一句:

    mRecyclerView.addItemDecoration(new DividerItemDecoration(this,
    DividerItemDecoration.VERTICAL_LIST));

    ok,现在再运行,就可以看到分割线的效果了。

    20150415145931083

    该分割线是系统默认的,你可以在theme.xml中找到该属性的使用情况。那么,使用系统的listDivider有什么好处呢?就是方便我们去随意的改变,该属性我们可以直接声明在:

     <!-- Application theme. -->
        <style name="AppTheme" parent="AppBaseTheme">
          <item name="android:listDivider">@drawable/divider_bg</item>  
        </style>

    然后自己写个drawable即可,下面我们换一种分隔符:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle" >
    
        <gradient
            android:centerColor="#ff00ff00"
            android:endColor="#ff0000ff"
            android:startColor="#ffff0000"
            android:type="linear" />
        <size android:height="4dp"/>
    
    </shape>

    现在的样子是:

    20150415151013289

    当然了,你可以根据自己的需求,去随意的绘制,反正是画出来的,随便玩~~

    ok,看到这,你可能觉得,这玩意真尼玛麻烦,完全不能比拟的心爱的ListView。那么继续看。

    LayoutManager

    好了,上面实现了类似ListView样子的Demo,通过使用其默认的LinearLayoutManager。

    RecyclerView.LayoutManager吧,这是一个抽象类,好在系统提供了3个实现类:

    1. LinearLayoutManager 现行管理器,支持横向、纵向。
    2. GridLayoutManager 网格布局管理器
    3. StaggeredGridLayoutManager 瀑布就式布局管理器

    上面我们已经初步体验了下LinearLayoutManager,接下来看GridLayoutManager。

    • GridLayoutManager

    我们尝试去实现类似GridView,秒秒钟的事情:

    //mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
            mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));

    只需要修改LayoutManager即可,还是很nice的。

    当然了,改为GridLayoutManager以后,对于分割线,前面的DividerItemDecoration就不适用了,主要是因为它在绘制的时候,比如水平线,针对每个child的取值为:

    final int left = parent.getPaddingLeft();
    final int right = parent.getWidth() - parent.getPaddingRight();

    因为每个Item一行,这样是没问题的。而GridLayoutManager时,一行有多个childItem,这样就多次绘制了,并且GridLayoutManager时,Item如果为最后一列(则右边无间隔线)或者为最后一行(底部无分割线)。

    针对上述,我们编写了DividerGridItemDecoration

    package com.zhy.sample.demo_recyclerview;
    
    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.GridLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.RecyclerView.LayoutManager;
    import android.support.v7.widget.RecyclerView.State;
    import android.support.v7.widget.StaggeredGridLayoutManager;
    import android.view.View;
    
    /**
     * 
     * @author zhy
     * 
     */
    public class DividerGridItemDecoration extends RecyclerView.ItemDecoration
    {
    
        private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
        private Drawable mDivider;
    
        public DividerGridItemDecoration(Context context)
        {
            final TypedArray a = context.obtainStyledAttributes(ATTRS);
            mDivider = a.getDrawable(0);
            a.recycle();
        }
    
        @Override
        public void onDraw(Canvas c, RecyclerView parent, State state)
        {
    
            drawHorizontal(c, parent);
            drawVertical(c, parent);
    
        }
    
        private int getSpanCount(RecyclerView parent)
        {
            // 列数
            int spanCount = -1;
            LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager)
            {
    
                spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
            } else if (layoutManager instanceof StaggeredGridLayoutManager)
            {
                spanCount = ((StaggeredGridLayoutManager) layoutManager)
                        .getSpanCount();
            }
            return spanCount;
        }
    
        public void drawHorizontal(Canvas c, RecyclerView parent)
        {
            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.getLeft() - params.leftMargin;
                final int right = child.getRight() + params.rightMargin
                        + mDivider.getIntrinsicWidth();
                final int top = child.getBottom() + params.bottomMargin;
                final int bottom = top + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
    
        public void drawVertical(Canvas c, RecyclerView parent)
        {
            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.getTop() - params.topMargin;
                final int bottom = child.getBottom() + params.bottomMargin;
                final int left = child.getRight() + params.rightMargin;
                final int right = left + mDivider.getIntrinsicWidth();
    
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
    
        private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
                int childCount)
        {
            LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager)
            {
                if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
                {
                    return true;
                }
            } else if (layoutManager instanceof StaggeredGridLayoutManager)
            {
                int orientation = ((StaggeredGridLayoutManager) layoutManager)
                        .getOrientation();
                if (orientation == StaggeredGridLayoutManager.VERTICAL)
                {
                    if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
                    {
                        return true;
                    }
                } else
                {
                    childCount = childCount - childCount % spanCount;
                    if (pos >= childCount)// 如果是最后一列,则不需要绘制右边
                        return true;
                }
            }
            return false;
        }
    
        private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
                int childCount)
        {
            LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager)
            {
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount)// 如果是最后一行,则不需要绘制底部
                    return true;
            } else if (layoutManager instanceof StaggeredGridLayoutManager)
            {
                int orientation = ((StaggeredGridLayoutManager) layoutManager)
                        .getOrientation();
                // StaggeredGridLayoutManager 且纵向滚动
                if (orientation == StaggeredGridLayoutManager.VERTICAL)
                {
                    childCount = childCount - childCount % spanCount;
                    // 如果是最后一行,则不需要绘制底部
                    if (pos >= childCount)
                        return true;
                } else
                // StaggeredGridLayoutManager 且横向滚动
                {
                    // 如果是最后一行,则不需要绘制底部
                    if ((pos + 1) % spanCount == 0)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    
        @Override
        public void getItemOffsets(Rect outRect, int itemPosition,
                RecyclerView parent)
        {
            int spanCount = getSpanCount(parent);
            int childCount = parent.getAdapter().getItemCount();
            if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部
            {
                outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
            } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边
            {
                outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
            } else
            {
                outRect.set(0, 0, mDivider.getIntrinsicWidth(),
                        mDivider.getIntrinsicHeight());
            }
        }
    }
    

    主要在getItemOffsets方法中,去判断如果是最后一行,则不需要绘制底部;如果是最后一列,则不需要绘制右边,整个判断也考虑到了StaggeredGridLayoutManager的横向和纵向,所以稍稍有些复杂。最重要还是去理解,如何绘制什么的不重要。一般如果仅仅是希望有空隙,还是去设置item的margin方便。

    最后的效果是:

    20150415150026088

    ok,看到这,你可能还觉得RecyclerView不够强大?

    但是如果我们有这么个需求,纵屏的时候显示为ListView,横屏的时候显示两列的GridView,我们RecyclerView可以轻松搞定,而如果使用ListView去实现还是需要点功夫的~~~

    当然了,这只是皮毛,下面让你心服口服。

    • StaggeredGridLayoutManager

    瀑布流式的布局,其实他可以实现GridLayoutManager一样的功能,仅仅按照下列代码:

    // mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));
            mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,        StaggeredGridLayoutManager.VERTICAL));

    这两种写法显示的效果是一致的,但是注意StaggeredGridLayoutManager构造的第二个参数传一个orientation,如果传入的是StaggeredGridLayoutManager.VERTICAL代表有多少列;那么传入的如果是StaggeredGridLayoutManager.HORIZONTAL就代表有多少行,比如本例如果改为:

    mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,
            StaggeredGridLayoutManager.HORIZONTAL));

    那么效果为:

    20150415150125431

    可以看到,固定为4行,变成了左右滑动。有一点需要注意,如果是横向的时候,item的宽度需要注意去设置,毕竟横向的宽度没有约束了,应为控件可以横向滚动了。
    如果你需要一样横向滚动的GridView,那么恭喜你。

    ok,接下来准备看大招,如果让你去实现个瀑布流,最起码不是那么随意就可以实现的吧?但是,如果使用RecyclerView,分分钟的事。
    那么如何实现?其实你什么都不用做,只要使用StaggeredGridLayoutManager我们就已经实现了,只是上面的item布局我们使用了固定的高度,下面我们仅仅在适配器的onBindViewHolder方法中为我们的item设置个随机的高度(代码就不贴了,最后会给出源码下载地址),看看效果图:

    20150415193645985

    是不是棒棒哒,通过RecyclerView去实现ListView、GridView、瀑布流的效果基本上没有什么区别,而且可以仅仅通过设置不同的LayoutManager即可实现。

    还有更nice的地方,就在于item增加、删除的动画也是可配置的。接下来看一下ItemAnimator。

    ItemAnimator

    ItemAnimator也是一个抽象类,好在系统为我们提供了一种默认的实现类,期待系统多
    添加些默认的实现。

    借助默认的实现,当Item添加和移除的时候,添加动画效果很简单:

    // 设置item动画
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());

    系统为我们提供了一个默认的实现,我们为我们的瀑布流添加以上一行代码,效果为:

    20150415194114149

    如果是GridLayoutManager呢?动画效果为:

    20150415150130083

    注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是
    notifyItemInserted(position)notifyItemRemoved(position)
    否则没有动画效果。
    上述为adapter中添加了两个方法:

    public void addData(int position) {
            mDatas.add(position, "Insert One");
            notifyItemInserted(position);
        }
    
        public void removeData(int position) {
                mDatas.remove(position);
            notifyItemRemoved(position);
        }

    Activity中点击MenuItem触发:

        @Override
        public boolean onCreateOptionsMenu(Menu menu)
        {
            getMenuInflater().inflate(R.menu.main, menu);
            return super.onCreateOptionsMenu(menu);
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item)
        {
            switch (item.getItemId())
            {
            case R.id.id_action_add:
                mAdapter.addData(1);
                break;
            case R.id.id_action_delete:
                mAdapter.removeData(1);
                break;
            }
            return true;
        }

    好了,到这我对这个控件已经不是一般的喜欢了~~~

    当然了只提供了一种动画,那么我们肯定可以去自定义各种nice的动画效果。
    高兴的是,github上已经有很多类似的项目了,这里我们直接引用下:RecyclerViewItemAnimators,大家自己下载查看。
    提供了SlideInOutLeftItemAnimator,SlideInOutRightItemAnimator,
    SlideInOutTopItemAnimator,SlideInOutBottomItemAnimator等动画效果。

    Click and LongClick

    不过一个挺郁闷的地方就是,系统没有提供ClickListener和LongClickListener。
    不过我们也可以自己去添加,只是会多了些代码而已。
    实现的方式比较多,你可以通过mRecyclerView.addOnItemTouchListener去监听然后去判断手势,
    当然你也可以通过adapter中自己去提供回调,这里我们选择后者,前者的方式,大家有兴趣自己去实现。

    那么代码也比较简单:

    class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
    {
    
    //...
        public interface OnItemClickLitener
        {
            void onItemClick(View view, int position);
            void onItemLongClick(View view , int position);
        }
    
        private OnItemClickLitener mOnItemClickLitener;
    
        public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
        {
            this.mOnItemClickLitener = mOnItemClickLitener;
        }
    
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position)
        {
            holder.tv.setText(mDatas.get(position));
    
            // 如果设置了回调,则设置点击事件
            if (mOnItemClickLitener != null)
            {
                holder.itemView.setOnClickListener(new OnClickListener()
                {
                    @Override
                    public void onClick(View v)
                    {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickLitener.onItemClick(holder.itemView, pos);
                    }
                });
    
                holder.itemView.setOnLongClickListener(new OnLongClickListener()
                {
                    @Override
                    public boolean onLongClick(View v)
                    {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                        return false;
                    }
                });
            }
        }
    //...
    }
    

    adapter中自己定义了个接口,然后在onBindViewHolder中去为holder.itemView去设置相应
    的监听最后回调我们设置的监听。

    最后别忘了给item添加一个drawable:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android" >
        <item android:state_pressed="true" android:drawable="@color/color_item_press"></item>
        <item android:drawable="@color/color_item_normal"></item>
    </selector>
    

    Activity中去设置监听:

    
            mAdapter.setOnItemClickLitener(new OnItemClickLitener()
            {
    
                @Override
                public void onItemClick(View view, int position)
                {
                    Toast.makeText(HomeActivity.this, position + " click",
                            Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onItemLongClick(View view, int position)
                {
                    Toast.makeText(HomeActivity.this, position + " long click",
                            Toast.LENGTH_SHORT).show();
                            mAdapter.removeData(position);
                }
            });
    

    测试效果:

    20150415194800546

    ok,到此我们基本介绍了RecylerView常见用法,包含了:

    • 系统提供了几种LayoutManager的使用;
    • 如何通过自定义ItemDecoration去设置分割线,或者一些你想作为分隔的drawable,注意这里
      巧妙的使用了系统的listDivider属性,你可以尝试添加使用divider和dividerHeight属性。
    • 如何使用ItemAnimator为RecylerView去添加Item移除、添加的动画效果。
    • 介绍了如何添加ItemClickListener与ItemLongClickListener。

    可以看到RecyclerView可以实现:

    整个体验下来,感觉这种插拔式的设计太棒了,如果系统再能提供一些常用的分隔符,多添加些动画效果就更好了。

    通过简单改变下LayoutManager,就可以产生不同的效果,那么我们可以根据手机屏幕的宽度去动态设置LayoutManager,屏幕宽度一般的,显示为ListView;宽度稍大的显示两列的GridView或者瀑布流(或者横纵屏幕切换时变化,有点意思~);显示的列数和宽度成正比。甚至某些特殊屏幕,让其横向滑动~~再选择一个nice的动画效果,相信这种插件式的编码体验一定会让你迅速爱上RecyclerView。

    参考资料

    Android 自定义RecyclerView 实现真正的Gallery效果

    A First Glance at Android’s RecyclerView

    https://github.com/gabrielemariotti/RecyclerViewItemAnimators

    DividerItemDecoration

    群号:423372824
    源码下载
    微信公众号:hongyangAndroid
    (欢迎关注,第一时间推送博文信息)
    1422600516_2905.jpg

    展开全文
  • androidx.recyclerview:recyclerview的使用

    万次阅读 多人点赞 2019-06-10 18:18:12
    添加扩展 或手动修改app/build.gradle...implementation 'androidx.recyclerview:recyclerview:1.0.0' 新建布局layout item布局fruit_item.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...

    添加扩展


    或手动修改app/build.gradle
    dependencies里添加

    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    

    新建布局layout

    item布局fruit_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/fruit_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp"/>
    </LinearLayout>
    

    修改活动布局activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </LinearLayout>
    

    item数据类

    package com.example.recyclerviewtest;
    
    
    public class Fruit {
        private String name;
        private int imageId;
    
        public Fruit(String name, int imageId) {
            this.name = name;
            this.imageId = imageId;
        }
    
        public String getName() {
            return name;
        }
    
        public int getImageId() {
            return imageId;
        }
    }
    
    

    适配器类FruitAdapter

    package com.example.recyclerviewtest;
    
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;
    
    import java.util.List;
    
    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
        private List<Fruit> mFruitList;
    
        static class ViewHolder extends RecyclerView.ViewHolder {
            View fruitView;
            ImageView fruitImage;
            TextView fruitName;
    
            public ViewHolder(View view) {
                super(view);
                fruitView = view;
                fruitImage = view.findViewById(R.id.fruit_image);
                fruitName = view.findViewById(R.id.fruit_name);
            }
        }
    
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.fruit_item, parent, false);
            final ViewHolder holder = new ViewHolder(view);
            holder.fruitView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int position = holder.getAdapterPosition();
                    Fruit fruit = mFruitList.get(position);
                    Toast.makeText(view.getContext(), "你点击了View"+ fruit.getName(), Toast.LENGTH_SHORT).show();
                }
            });
            holder.fruitImage.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int position = holder.getAdapterPosition();
                    Fruit fruit = mFruitList.get(position);
                    Toast.makeText(view.getContext(), "你点击了图片"+ fruit.getName(), Toast.LENGTH_SHORT).show();
                }
            });
            return holder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            Fruit fruit = mFruitList.get(position);
            holder.fruitImage.setImageResource(fruit.getImageId());
            holder.fruitName.setText(fruit.getName());
        }
    
        @Override
        public int getItemCount() {
            return mFruitList.size();
        }
    
        public FruitAdapter(List<Fruit> fruitList) {
            mFruitList = fruitList;
        }
    
    }
    

    主活动类

    package com.example.recyclerviewtest;
    
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.recyclerview.widget.LinearLayoutManager;
    import androidx.recyclerview.widget.RecyclerView;
    
    import android.os.Bundle;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private List<Fruit> fruitList = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initFruits();
            RecyclerView recyclerView = findViewById(R.id.recycler_view);
            LinearLayoutManager layoutManager= new LinearLayoutManager(this);
            recyclerView.setLayoutManager(layoutManager);
            FruitAdapter adapter = new FruitAdapter(fruitList);
            recyclerView.setAdapter(adapter);
        }
    
        private void initFruits() {
            for(int i = 0; i < 10; i++) {
                Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
                fruitList.add(apple);
                Fruit banana = new Fruit("banana", R.drawable.banana_pic);
                fruitList.add(banana);
            }
        }
    }
    

    资源文件可以自己去弄,效果:

    展开全文
  • Recyclerview嵌套Recyclerview案列

    千次下载 热门讨论 2016-05-27 18:20:43
    Recyclerview嵌套Recyclerview ,外层列表,内层网格,支持上拉下拉刷新;Recyclerview嵌套Recyclerview ,外层列表,内层网格,支持上拉下拉刷新
  • RecyclerView嵌套RecyclerView

    万次阅读 热门讨论 2016-05-27 18:26:25
    ListView嵌套GridView http://blog.csdn.net/baiyuliang2013/article/details/42646289RecyclerView下拉刷新上拉加载(一)http://blog.csdn.net/baiyuliang2013/article/details/51506036RecyclerView下拉刷新上拉...

    ListView嵌套GridView http://blog.csdn.net/baiyuliang2013/article/details/42646289

    RecyclerView下拉刷新上拉加载(一)http://blog.csdn.net/baiyuliang2013/article/details/51506036

    RecyclerView下拉刷新上拉加载(二)http://blog.csdn.net/baiyuliang2013/article/details/51506354

    RecyclerView下拉刷新上拉加载(三)http://blog.csdn.net/baiyuliang2013/article/details/51516727

    延伸:仿微信、QQ评论点击事件

    趁热打铁,接着前几篇继续,不说废话了,先上图,看看本篇文章要实现的是什么效果:

    这里写图片描述这里写图片描述

    之前实现过ListView嵌套GridView实现类似的效果,那么既然这几篇一直在讲RecylerView,并且RecylerView这么强大,那就用RecylerView实现以下呗!

    实现思路:

    1.在外层的item布局中放一个RecyclerView用来显示网格布局:

    这里写图片描述

    2.增加一个ImageAdapter,用来展示图片;

    3.在原InfoAdapter中对图片数据进行处理:

      if(infoBean.getImgList()!=null&&infoBean.getImgList().size()>0){
                ImageAdapter imageAdapter = new ImageAdapter(context, infoBean.getImgList());
                holder.rv_grid.setLayoutManager(new GridLayoutManager(context,3));
                holder.rv_grid.setAdapter(imageAdapter);
                holder.rv_grid.setVisibility(View.VISIBLE);
            }else{
                holder.rv_grid.setVisibility(View.GONE);
            }

    注意判断图片列表是否为空的情况,为空时不显示网格布局!

    好了,运行代码,代码运行起来后,有一个奇怪的问题,就是网格布局只显示了一行也就是三张图片,可我明明设置了6张,打印下log后,确定adapter中6张是全部加载了,但却没显示完全!

    经过测试最终找到原因,是把网格布局item中的父控件和imageview全部设置设置成了match_parent,而更换成一个固定值后,成功了!6张图片显示完全!可又出现了一个问题,因为你并不知道屏幕宽度,而在xml中设置固定宽高后必然导致布局中图片宽高显示不均的问题!如果大家看过之前的ListView嵌套GridView的文章,那么就知道此时我们该怎么做了!对,就是动态计算网格布局中ImageView的宽高:

    在ImageAdapter中:

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = mInflater.inflate(R.layout.item_grid, parent, false);
            //动态设置ImageView的宽高,根据自己每行item数量计算
            //dm.widthPixels-dip2px(20)即屏幕宽度-左右10dp+10dp=20dp再转换为px的宽度,最后/3得到每个item的宽高
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams((dm.widthPixels - dip2px(20)) / 3, (dm.widthPixels - dip2px(20)) / 3);
            view.setLayoutParams(lp);
            return new MyViewHolder(view);
        }

    屏幕宽度-左右边距宽度/每行的item数量(如果设置有间距,还要减掉间距)即可得到一个宽度,然后设置view的宽高值,便可得到一个标准的正方形图片!

    图片的点击事件并没有添加,只需在ImageAdapter中添加即可:

      @Override
        public void onBindViewHolder(MyViewHolder holder, final int position) {
            super.onBindViewHolder(holder, position);
            String url = (String) listDatas.get(position);//转换
            fb.display(holder.iv, url);
            holder.iv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context, "position>>"+position, Toast.LENGTH_SHORT).show();
                }
            });
        }

    例子比较简单,其它需求可自行添加!

    ASdemo下载地址:http://download.csdn.net/detail/baiyuliang2013/9533422

    展开全文
  • RecyclerView嵌套RecyclerView滑动冲突
  • 如图首页整个界面是一个RecyclerView,根据不同的type创建不同的view来显示: RecyclerView常见问题解决方案: 1:首先查看RecyclerView在设置Adapter前有没有被LayoutManager管理(如果没有肯定连显示都不显示...
  • RecyclerView 嵌套 RecyclerView 滑动冲突解决----------------------- 本人亲测
  • RecyclerView嵌套RecyclerView问题 (一) 嵌套刷新后分割间距变化问题 场景:RecyclerView嵌套RecyclerView,且外部增加下拉刷新,运用addItemDecoration分别添加分割,刷新后内部列表分割间距变大,外部列表不变 ...
  • RecyclerView 通用适配 BaseQuickAdapter

    万次阅读 热门讨论 2017-04-05 11:11:15
    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比...RecyclerView 同样也用到适配,枯燥重复的适配肯定会让你不胜其烦,下面让我们一起来打造一款通用的适配(BaseQui...
  • Android RecyclerView嵌套RecyclerView

    千次阅读 2018-10-08 09:36:15
    RecyclerView嵌套RecyclerView的条目,项目中可能会经常有这样的需求,但是我们将子条目设置为RecyclerView之后,却显示不出来。自己试了很久,终于找到了原因:必须先设置子RecylcerView的高度。你要花精力确定出子...
  • Android中RecyclerView嵌套RecyclerView

    热门讨论 2016-07-08 18:20:55
    RecyclerView是v7包中的一个控件非常实用,尤其是嵌套,这是我在网上找的一个例子,放在这希望和大家下载交流。
  • 主要为大家详细介绍了RecyclerView嵌套RecyclerView滑动卡顿的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • RecyclerView用法

    千次下载 热门讨论 2016-03-07 14:31:00
    博客地址:http://blog.csdn.net/dmk877/article/details/50816933,详细讲解了RecyclerView的用法
  • TvRecyclerView

    千次阅读 2019-01-14 17:10:42
    TvRecyclerView 项目地址:henryblue/TvRecyclerView  简介:A custom RecyclerView for Android TV end 更多:作者 提 Bug  标签: tv选中效果- A custom RecyclerView for Android TV end.中文说明 ...
  • RecyclerView 嵌套RecyclerView,各自响应滑动事件
  • 问题场景 当多个RecyclerView嵌套,只给外部...重写内部RecyclerView touch事件 并 return 外部RecyclerView item的onTouchEvnet事件 recyclerView.setOnTouchListener(object : View.OnTouchListener { override fun o
  • RecyclerView定位

    千次下载 热门讨论 2015-10-18 16:50:36
    RecyclerView Item置顶 博客地址:http://blog.csdn.net/tyzlmjj/article/details/49227601
  • 在做项目中商品属性选择弹窗时,使用RecyclerView嵌套RecyclerView实现功能时,单项父RecyclerView item内容高度为wrap_content却没有将子RecyclerView内容显示完全,只显示了三行,如图: 一开始总觉得是父item的...
  • RecyclerView填充数据页面跳动问题, 通常是因为切套了RecyclerView 处理方法:在子RecyclerView中添加 recyclerView.setFocusableInTouchMode(false); recyclerView.requestFocus(); ...
  • 需要重写上一层的recyclerview public class ParentRecyclerView extends RecyclerView { public ParentRecyclerView(@NonNull Context context) { super(context); } public ParentRecyclerView(@NonNull ...
  • RecyclerView.jar

    千次下载 热门讨论 2014-08-15 10:38:03
    RecyclerView.jar 添加到libs中能让你使用组件RecylcerView
  • 1 删除整个RecyclerView hisList.clear(); hisAdapter.notifyDataSetChanged(); 刚开始只是使用了list.clear() 方法就是没有效果,最后想起来了忘记了notifyDataSetChanged 刷新了 2RecyclerView...
  • RecyclerView 在本文发布之前,已经出来很久了,之前也有很多关于 RecyclerView 的优秀文章,那我为什么还要在这里写下本文呢,实不相瞒,我是 ListView 的忠实粉,之前很少使用到 RecyclerView,最近发现了不少关于...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,357
精华内容 16,942
关键字:

recyclerview