Android的RecyclerView的初体验

本文详细介绍了如何在Android项目中使用RecyclerView替代ListView,包括添加分割线、实现回调监听、添加点击水波纹效果等基本操作,并提供了一套完整的示例代码。同时,解释了如何通过自定义Adapter来展示数据,以及如何获取屏幕尺寸来适配不同设备。

前言:RecyclerView出来已经很久了,一直在用listView,也来尝试者在项目用一次RecyclerView,把一些心得拿出来分享一下,这里只用RecyclerView来做一个简单的列表展示,感觉还没有体现他的强大之处,相比listView,它少了分割线,点击后背景的变化,点击的回调接口,已经多了些设置。这里将:

  1. 列举出使用RecyclerView的基本步骤。
  2. 为RecyclerView添加分割线。
  3. 添加回调监听接口。
  4. 添加点击条目的水波纹效果。

一,使用RecyclerView的基本步骤:

  1. 添加gradle支持:
    compile 'com.android.support:recyclerview-v7:23.1.1'
  2. 在布局文件中使用:
    <android.support.v7.widget.RecyclerView
                    android:id="@+id/rv_left_menu_content"
                    android:layout_width="match_parent"
                    android:layout_height="240dp"/>
  3. 编写条目的item;
    <?xml version="1.0" encoding="utf-8"?>
    <!--这里是引用了水波纹效果的布局,父类是一个linearLayout下面会展示源码-->
    <com.wjustudio.phoneManager.widgt.RevealLayout
        android:id="@+id/item_home"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:clickable="true"
        android:background="@drawable/left_item_selector"
        android:gravity="center_vertical">
    
        <ImageView
            android:id="@+id/img_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:src="@mipmap/icon_app"/>
    
        <TextView
            android:id="@+id/tv_icon_name"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:layout_toRightOf="@id/img_icon"
            android:text="应用管理"
            android:textColor="@color/textColor"
            android:textSize="20sp"/>
    </com.wjustudio.phoneManager.widgt.RevealLayout></span>
    
    
  4. 在activity中使用,并添加相应的配置:
    //先通过findviewbyid或注解的方式得到RecyclerView的对象:mRvLeftMenuContent
    //设置RecyclerView
    // 创建一个线性布局管理器
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    //设置垂直滚动,也可以设置横向滚动
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    //RecyclerView设置布局管理器
    mRvLeftMenuContent.setLayoutManager(layoutManager);
    //设置侧拉菜单的recyclerView的布局
    mRvLeftMenuContent.setAdapter(new LeftMenuAdapter(mContext, mLeftPageIcons, windowSize));
    

  5. 编写adapter:
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    import com.wjustudio.phoneManager.R;
    import com.wjustudio.phoneManager.javaBean.IconInfo;
    
    import java.util.HashMap;
    import java.util.List;
    
    /**
     * 左侧菜单对应的viewHolder
     */
    public class LeftMenuAdapter extends RecyclerView.Adapter {
        private Context mContext;
        private List<IconInfo> mLeftPageIcons;
        private int mWindowHeight;
        private int mWindowWidth;
        
        public LeftMenuAdapter(Context context, List<IconInfo> leftPageIcons,  HashMap<String, Integer> windowSize) {
            mContext = context;
            mLeftPageIcons = leftPageIcons;
            mWindowHeight = windowSize.get("height");
            mWindowWidth = windowSize.get("width");
        }
    
    
        /**
         * 自定义的ViewHolder,持有每个Item的的所有界面元素
         */
        public class NormalViewHolder extends RecyclerView.ViewHolder {
            ImageView icon;
            TextView iconName;
    
            public NormalViewHolder(View itemView) {
                super(itemView);
                icon = (ImageView) itemView.findViewById(R.id.img_icon);
                iconName = (TextView) itemView.findViewById(R.id.tv_icon_name);
                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) icon.getLayoutParams();
                params.height = mWindowHeight / 9;
                icon.setLayoutParams(params);
                params = (LinearLayout.LayoutParams) iconName.getLayoutParams();
                params.width = mWindowWidth - icon.getWidth();
                iconName.setLayoutParams(params);
            }
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            //在该方法中我们创建一个ViewHolder并返回,ViewHolder必须有一个带有View的构造函数,
            //这个View就是我们Item的根布局,在这里我们使用自定义Item的布局;
            View item = View.inflate(mContext, R.layout.item_left, null);
            return new NormalViewHolder(item);
        }
    
        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
            //将数据与界面进行绑定的操作
            NormalViewHolder normalHolder = (NormalViewHolder) holder;
            normalHolder.icon.setImageResource(mLeftPageIcons.get(position).icon);
            normalHolder.iconName.setText(mLeftPageIcons.get(position).iconName);
    
            }
        }
    
        @Override
        public int getItemCount() {
            //获取数据的数量
            return mLeftPageIcons == null ? 0 : mLeftPageIcons.size();
        }
    }

             windowSize放的是屏幕的宽高,这里是为了设置条目的宽度,是这样的的到的:

            

   /**
     * 获得宽度和除去通知栏的屏幕的高度
     * @param activity
     * @return
     */
    public static HashMap<String,Integer> getWindowSize(Activity activity){
        WindowManager wm = activity.getWindowManager();
        DisplayMetrics metrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(metrics);
        int width = metrics.widthPixels;
        int height = metrics.heightPixels;
        height -= getStatusBarHeight(activity);
        HashMap<String,Integer> windowSize = new HashMap<>();
        windowSize.put("height",height);
        windowSize.put("width",width);
        return windowSize;
    }

    /**
     * 获得状态栏的高度
     * @param activity
     * @return
     */
    public static int getStatusBarHeight(Activity activity){
        int statusHeight = 0;
        Rect frame = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        statusHeight = frame.top;
        if (0 == statusHeight){
            Class<?> localClass;
            try {
                localClass = Class.forName("com.android.internal.R$dimen");
                Object localObject = localClass.newInstance();
                int i5 = Integer.parseInt(localClass.getField("status_bar_height").
                        get(localObject).toString());
                statusHeight = activity.getResources().getDimensionPixelSize(i5);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return statusHeight;
    }

二,为RecyclerView添加分割线:

编写类

package com.wjustudio.phoneManager.widgt;

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;


/**
 * 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) {
        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);
        }
    }
}

在RecyclerView中添加:

//添加分割线
mRvLeftMenuContent.addItemDecoration(new DividerItemDecoration(
        this, DividerItemDecoration.VERTICAL_LIST
));

三,添加回调监听接口:

   /**
     * 点击事件的监听接口
     */
    public interface OnItemClickListener{
        void onItemClick(View view, int position);
    }

    private OnItemClickListener mOnItemClickLitener;

    public void setOnItemClicklistener(OnItemClickListener listener){
        mOnItemClickLitener = listener;
    }

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        //将数据与界面进行绑定的操作
        NormalViewHolder normalHolder = (NormalViewHolder) holder;
        normalHolder.icon.setImageResource(mLeftPageIcons.get(position).icon);
        normalHolder.iconName.setText(mLeftPageIcons.get(position).iconName);

        if (mOnItemClickLitener != null){
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClickLitener.onItemClick(holder.itemView,position);
                }
            });
        }
}

四,添加条目点击的水波纹效果:

package com.wjustudio.phoneManager.widgt;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

import com.wjustudio.phoneManager.R;

import java.util.ArrayList;

public class RevealLayout extends LinearLayout implements Runnable{
	private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	
	private float mCenterX,mCenterY;
	
	private int[] mLocation = new int[2];
	
	private int INVALIDATE_DURATION = 40;
	private int mTargetHeight,mTargetWidth;
	private int mRevealRadius = 0,mRevealRadiusGap,mMaxRadius;
	private int mMinBetweenWidthAndHeight,mMaxBetweenWidthAndHeight;
	
	private boolean mIsPressed;
	private boolean mShouldDoAnimation;
	
	private View mTargetView;
	private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable();
	
	public RevealLayout(Context context) {
		super(context);
		init();
	}
	
	public RevealLayout(Context context, AttributeSet attrs){
		super(context,attrs);
		init();
	}
	
	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	public RevealLayout(Context context, AttributeSet attrs, int defStyleAttr){
		super(context,attrs,defStyleAttr);
		init();
	}

	public void init(){
		setWillNotDraw(false);
		mPaint.setColor(getResources().getColor(R.color.reveal_color));
	}
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		this.getLocationOnScreen(mLocation);
	}
	
	@Override
	protected void dispatchDraw(Canvas canvas) {
		super.dispatchDraw(canvas);
		
		if(mTargetView == null || !mShouldDoAnimation || mTargetWidth <= 0)
			return;
		
		if(mRevealRadius > mMinBetweenWidthAndHeight / 2)
			mRevealRadius += mRevealRadiusGap * 4;
		else
			mRevealRadius += mRevealRadiusGap;
		
		int[] location = new int[2];
		this.getLocationOnScreen(mLocation);
		mTargetView.getLocationOnScreen(location);

		int top = location[1] - mLocation[1];
		int left = location[0] - mLocation[0];
		int right = left + mTargetView.getMeasuredWidth();
		int bottom = top + mTargetView.getMeasuredHeight();
		
		canvas.save();
		canvas.clipRect(left, top, right, bottom);
		canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);
		canvas.restore();
		
		if(mRevealRadius <= mMaxRadius)
			postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
		else if(!mIsPressed){
			mShouldDoAnimation = false;
			postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
		}
	}
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		int x = (int)event.getRawX();
		int y = (int)event.getRawY();
		int action = event.getAction();
		
		switch(action){
			case MotionEvent.ACTION_DOWN:
				View targetView = getTargetView(this,x,y);
				
				if(targetView != null && targetView.isEnabled()){
					mTargetView = targetView;
					initParametersForChild(event,targetView);
					postInvalidateDelayed(INVALIDATE_DURATION);
				}
				break;
				
			case MotionEvent.ACTION_UP:
				mIsPressed = false;
				postInvalidateDelayed(INVALIDATE_DURATION);
				mDispatchUpTouchEventRunnable.event = event;
				postDelayed(mDispatchUpTouchEventRunnable, 40);
				break;
				
			case MotionEvent.ACTION_CANCEL:
				mIsPressed = false;
				postInvalidateDelayed(INVALIDATE_DURATION);
				break;
		}		
		return super.dispatchTouchEvent(event);
	}
	
	public View getTargetView(View view,int x,int y){
		View target = null;
		ArrayList<View> views = view.getTouchables();
		
		for(View child : views)
			if(isTouchPointInView(child,x,y)){
				target = child;
				break;
			}
		return target;
	}
	
	public boolean isTouchPointInView(View child,int x,int y){
		int[] location = new int[2];
		child.getLocationOnScreen(location);

		int top = location[1];
		int left = location[0];
		int right = left + child.getMeasuredWidth();
		int bottom = top + child.getMeasuredHeight();
		
		if(child.isClickable() && y>=top && y<= bottom && x >= left && x<= right)
			return true;
		else
			return false;
	}
	
	public void initParametersForChild(MotionEvent event,View view){
		mCenterX = event.getX();
		mCenterY = event.getY();
		mTargetWidth = view.getMeasuredWidth();
		mTargetHeight = view.getMeasuredHeight();
		mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);
		mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);

		mRevealRadius = 0;
		mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;

		mIsPressed = true;
		mShouldDoAnimation = true;
		
		int[] location = new int[2];
		view.getLocationOnScreen(location);
		
		int left = location[0] - mLocation[0];
		int mTransformedCenterX = (int)mCenterX - left;
		mMaxRadius = Math.max(mTransformedCenterX, mTargetWidth - mTransformedCenterX);
	}
	
	@Override
	public void run() {
		super.performClick();
	}

	@Override
	public boolean performClick() {
		postDelayed(this,40);
		return true;
	}
	private class DispatchUpTouchEventRunnable implements Runnable{
		public MotionEvent event;
		
		@Override
		public void run() {
			if(mTargetView.isEnabled() && mTargetView.isClickable())
				return;
			
			if(isTouchPointInView(mTargetView, (int)event.getRawX(), (int)event.getRawX()))
				mTargetView.performClick();
		}
	}
}

使用这个类作为条目的父控件就可以了。

点击的时候为了有颜色变化还应该为条目设置背景的selector:

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

对应的颜色值为:

 <drawable name="reveal_color">#1b000000</drawable>
 <drawable name="left_bg_color">#E7E5E4</drawable>


RecycleView中替代ListView中的setselection的方法:

((LinearLayoutManager)mRecyclerView.getLayoutManager()).scrollToPositionWithOffset(i,0);

其中的i表示第i个条目,0表示偏移的像素数。



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值