RecyclerView全面解析之一:基本使用和分割线的设置

写在前面

这个是已经替代了ListView、GridView和瀑布流效果的一个插件,针对这个东西的使用我觉得有必要写一点东西来加深我对它的了解,一点点来,首先就是基本使用和分割线的设置。

一、基本使用

既然已经有了ListView、GridView为什么还需要RecyclerView这样的控件呢?
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
首先我们来梳理一下他的基本使用:

功能点一:控制其显示的方式:通过布局管理器LayoutManager,ListView–>GridView–>瀑布流 只需要一行代码;

功能点二:控制item间的间隔,(可绘制),通过ItemDecoration(这个比较蛋疼)

功能点三:控制Item增删的动画,通过ItemAnimator;

功能点四:控制点击、长按事件,自己来写

二、具体实现

第一步:添加依赖:

implementation 'com.android.support:recyclerview-v7:27.1.1'

第二步:在xml中把recyclerView控件放进去:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
    android:id="@+id/recy_clidfragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

</android.support.v7.widget.RecyclerView>

</RelativeLayout>

第三步:实现功能点一:设置显示方式;
在通过Adapter来绑定数据源之前我们需要通过setLayoutManage来设置不同显示方式(ListView、GridView、瀑布流)。

// 设置recyclerView的布局管理  
    // LinearLayoutManager -> ListView风格
    // GridLayoutManager -> GridView风格
    // StaggeredGridLayoutManager -> 瀑布流风格
    LinearLayoutManager linearLayoutManager = new 
          LinearLayoutManager(this);
    mRecyclerView.setLayoutManager(linearLayoutManager);

第四步:基本Adapter设置数据的方式:

package com.lay.laykypro.adapter.ClidFragment;

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 com.lay.laykypro.R;

import java.util.HashMap;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;


public class MainClidRecyclerViewAdapter extends RecyclerView.Adapter<MainClidRecyclerViewAdapter.MainClidHolder> {

    private List<HashMap<String, Object>> mdataList;
    private Context mContext;
    private LayoutInflater mLayoutInflater;

    public MainClidRecyclerViewAdapter(List<HashMap<String, Object>> mdataList, Context mContext) {
        this.mdataList = mdataList;
        this.mContext = mContext;
        this.mLayoutInflater = mLayoutInflater.from(mContext);
    }

    @Override
    public MainClidHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mLayoutInflater.inflate(R.layout.item_recyclerview_clidfragment, parent, false);
        MainClidHolder mainClidHolder = new MainClidHolder(view);

        return mainClidHolder;
    }

    @Override
    public void onBindViewHolder(MainClidHolder holder, int position) {
            //设置数据绑定
        HashMap<String, Object> itemHashMap = mdataList.get(position);
        //拿到控件
        holder.tvRecyclerview.setText((String)itemHashMap.get("type"));

    }

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

    class MainClidHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.tv_recyclerview)
        TextView tvRecyclerview;


        public MainClidHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }


}

【额外备注:我们一定要额外注意==和equals的区别,我拿的数据是抓取开眼的数据,比较复杂,我就没办法直接用一个JavaBean来代表,所以我用Retrofit+Rxjava下载Json数据之后,我使用原生Json解析来取的数据,结果就是在==上面粗心大意,还在那检查了好几遍,,,真是尴尬。】
【参考博客:java中==和equal的区别 - 小皇帝POP - 博客园 https://www.cnblogs.com/pop822/p/6215040.html

言归正传,现在我们已经使用RecyclerView实现了简单ListView的功能,如下图:


RecyclerView基本使用ListView风格_new.jpg

第五步:实现分割线定制
RecyclerView并没有支持divider这样的属性。我们要实现他当然也可以通过直接在Item的布局中来写的方法,但是这样不太优雅,下面我们看看应该怎么在RecyclerView中定制一个它的分割线,代码如下:
1.首先我们要写一个类来继承一个RecyclerView.ItemDecoration,实现自定义分割线,代码如下:

package com.lay.laykypro.adapter.ClidFragment;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * 定制RecyclerView的分割线
 * 目标:10px的红色线
 */
public class MainClidItemDecoration extends RecyclerView.ItemDecoration {
    private Paint mPaint;

    public MainClidItemDecoration(int Color) {
        this.mPaint = new Paint();
        mPaint.setColor(Color);
        mPaint.setAntiAlias(true);
    }

    /**
     * 绘制分割线
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        //利用Canvas想绘制什么就绘制什么
        int childCount = parent.getChildCount();
        Rect rect = new Rect();
        rect.left=parent.getPaddingLeft();
        rect.right=parent.getWidth()-parent.getPaddingRight();
        for (int i = 1; i <childCount ; i++) {
            View view = parent.getChildAt(i);
            rect.bottom=view.getTop();
            rect.top=rect.bottom-10;

            c.drawRect(rect,mPaint);

        }

    }

    /**
     * 留出分割线位置
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
//        super.getItemOffsets(outRect, view, parent, state);
        //每个item下面流出10px(像素,单位可以单独找一篇博客来看看)来绘制分割线。 验证一号
        //思路一:最后一个item下面不需要分割线;失败
        //思路二:第一个上面不需要item的分割线;成功
        int position=parent.getChildAdapterPosition(view);
        //parent.getChildCount()是不断变化的,不能直接得到parent.getChildCount()的确定数字。所以思路一作废。
//        if (position!=parent.getChildCount()-1){//如果不是最后一个item
//            outRect.bottom=10;
//        }
        if (position!=0){
            outRect.top=10;
        }

    }
}

2.我们要将这个分割线设置到RecyclerView上面去:

 recyClidfragment.addItemDecoration(new MainClidItemDecoration(Color.RED));

结果如下:


RecyclerView分割线_red.jpg

注意:这里我写的分割线在第一个item的顶上和在最后一个item的底部没有。具体逻辑在代码中有请自行查阅。

3.使用Drawable来定制item分割线的方法。
Drawable资源代码:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <size android:height="0.5dp"   />
    <solid android:color="@color/list_item_divider" />
</shape>
或者
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient android:startColor="@color/colorAccent"
        android:centerColor="@color/colorBlue"
        android:endColor="@color/colorYellow"></gradient>
    <size android:height="10dp"   />
    
</shape>
或者
直接关联图片资源:
 recyClidfragment.addItemDecoration(new MainClidItemDecoration(getActivity(),R.drawable.heng));

color资源代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="list_item_divider">#ccccff</color>
 <color name="colorAccent">#FF4081</color>
 <color name="colorYellow">#ffff00</color>
 <color name="colorBlue">#66cccc</color>
</resources>


分割线类的代码如下:

package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * 定制RecyclerView的分割线
 * 目标:使用Drawable资源来定制分割线
 */
public class MainClidItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDrawable;

    public MainClidItemDecoration(Context mContext, int drawableResId) {
        this.mDrawable = ContextCompat.getDrawable(mContext,drawableResId);
    }

    /**
     * 绘制分割线
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        //利用Canvas想绘制什么就绘制什么
        int childCount = parent.getChildCount();
        Rect rect = new Rect();
        rect.left=parent.getPaddingLeft();
        rect.right=parent.getWidth()-parent.getPaddingRight();
        for (int i = 1; i <childCount ; i++) {
            View view = parent.getChildAt(i);
            rect.bottom=view.getTop();
            rect.top=rect.bottom-mDrawable.getIntrinsicHeight();

            mDrawable.setBounds(rect);
            mDrawable.draw(c);


        }

    }

    /**
     * 留出分割线位置
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
//        super.getItemOffsets(outRect, view, parent, state);
        //每个item下面流出10px(像素,单位可以单独找一篇博客来看看)来绘制分割线。 验证一号
        //思路一:最后一个item下面不需要分割线;失败
        //思路二:第一个上面不需要item的分割线;成功
        int position=parent.getChildAdapterPosition(view);
        //parent.getChildCount()是不断变化的,不能直接得到parent.getChildCount()的确定数字。所以思路一作废。
//        if (position!=parent.getChildCount()-1){//如果不是最后一个item
//            outRect.bottom=10;
//        }
        if (position!=0){
            outRect.top=mDrawable.getIntrinsicHeight();
        }

    }
}

因为Color的不同,结果如下图所示:
一种颜色的细线:


RecyclerView分割线_drawable_单色细线.jpg

三种颜色过度的粗线:


RecyclerView分割线_drawable_三色过度粗线.jpg

图片为资源的线:


RecyclerView分割线_图片.jpg

结论:实际情况证明,用drawable来写的方式效率更高,上面基本实现了ListView样式下的万能分割线

4.GridView样式下的万能分割线。
xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/colorBlue"></solid>
    <size android:height="1dp" android:width="1.5dp"  />

</shape>

java部分代码:

Fragment部分:
        //设置分割线
        MainClidItemGridDecoration mainClidItemGridDecoration = new MainClidItemGridDecoration(getActivity(), R.drawable.itemdecoration);
        recyClidfragment.addItemDecoration(mainClidItemGridDecoration);
分割线类:
package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.lay.laykypro.R;

public class MainClidItemGridDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDrawable;

    public MainClidItemGridDecoration(Context mContext,int drawableResId) {
        this.mDrawable = ContextCompat.getDrawable(mContext, drawableResId);
    }

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        //两个方向的绘制
        drawVertical(canvas,parent);
        drawHorizontal(canvas,parent);
    }

    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();

        Rect rect = new Rect();

        for (int i = 0; i <childCount ; i++) {
            View view = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();//使用这个是考虑到设置item中的背景间距的情况,在没有使用的时候这个部分可以删除。
            rect.left=view.getLeft()-layoutParams.leftMargin;
            rect.right=view.getRight()+layoutParams.rightMargin;
            rect.top=view.getBottom();
            rect.bottom=rect.top+mDrawable.getIntrinsicHeight();

            mDrawable.setBounds(rect);
            mDrawable.draw(c);


        }

    }

    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();

        Rect rect = new Rect();

        for (int i = 0; i <childCount ; i++) {
            View view = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();//使用这个是考虑到设置item中的背景间距的情况,在没有使用的时候这个部分可以删除。
            rect.left=view.getRight()+layoutParams.rightMargin;
            rect.right=rect.left+mDrawable.getIntrinsicWidth();
            rect.top=view.getTop()-layoutParams.topMargin-layoutParams.bottomMargin;
            rect.bottom=view.getBottom()+mDrawable.getIntrinsicHeight();//高是线的厚度;

            mDrawable.setBounds(rect);
            mDrawable.draw(c);


        }

    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.bottom=mDrawable.getIntrinsicHeight();
        outRect.right=mDrawable.getIntrinsicWidth();
    }
}


由于这次考虑了item的布局文件中设置了layout_margin属性,所以附上item代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_margin="20dp"
    android:background="@color/colorBlue"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/tv_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="Keep"
        android:textSize="45dp"/>

</RelativeLayout>

结果如下图:


RecyclerViewGridView_bug.jpg

注意:我目前的结果是最右边和最下边的item依然是有边框的,我们的目的应该是最右边和最下边item不能显示边框,在下一步解决它。

5.分割线的源码分析。
我打开了RecyclerView的源码,跟着网上一些大神的脚步,找到了关于分割线最重要的几部分源码,具体如下:
onDraw()
这段代码就是根据分割线的数量来不断的进行绘制。

    @Override
    public void onDraw(Canvas c) {
        super.onDraw(c);

        final int count = mItemDecorations.size();
        for (int i = 0; i < count; i++) {
            // 回调出去直接在getItemOffsets留出分割线位置的基础上直接绘制
            mItemDecorations.get(i).onDraw(c, this, mState);
        }
    }

measureChild()
这个方法就是通过getItemDecorInsetsForChild()得到的Rect的结果来计算出item的高宽。

public void measureChild(View child, int widthUsed, int heightUsed) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        //  getItemDecorInsetsForChild 这个是关键
        final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
        widthUsed += insets.left + insets.right;
        heightUsed += insets.top + insets.bottom;
        // 考虑分割线返回的Rect
        final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
            getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
            canScrollHorizontally());
        final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
            getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
            canScrollVertically());
        if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
            child.measure(widthSpec, heightSpec);
        }
    }

getItemDecorInsetsForChild()
这个方法是通过getItemOffsets()方法中设置的Rect对象的参数来测量出item的分割线的大小。

    Rect getItemDecorInsetsForChild(View child) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        if (!lp.mInsetsDirty) {
            return lp.mDecorInsets;
        }

        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
            // changed/invalid items should not be updated until they are rebound.
            return lp.mDecorInsets;
        }
        final Rect insets = lp.mDecorInsets;
        insets.set(0, 0, 0, 0);
        final int decorCount = mItemDecorations.size();
        for (int i = 0; i < decorCount; i++) {
            mTempRect.set(0, 0, 0, 0);
            // getItemOffsets()还是比较熟悉,获取分割线返回的占用位置
            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
            // 开始累加占用位置
            insets.left += mTempRect.left;
            insets.top += mTempRect.top;
            insets.right += mTempRect.right;
            insets.bottom += mTempRect.bottom;
        }
        lp.mInsetsDirty = false;
        // 返回分割线的Rect
        return insets;
    }

6.解决RecyclerView设置为GridView模式时的bug;
经过我各种查询,暂时将问题解决了一半,那就是,将最右边的一列的右侧分割线给去掉了,代码如下:

package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.lay.laykypro.R;

/**
 * 解决掉bug的GridView形式的分割线
 * 思路:写出两个方法分别判断是否是最后一排和最后一列即可
 */
public class MainClidItemGridDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDrawable;

    public MainClidItemGridDecoration(Context mContext,int drawableResId) {
        this.mDrawable = ContextCompat.getDrawable(mContext, drawableResId);
    }

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(canvas, parent, state);
        //两个方向的绘制
        drawVertical(canvas,parent);
        drawHorizontal(canvas,parent);
    }

    /**
     * 得到列数
     * @param parent
     * @return
     */
    private int getSpanCount(RecyclerView parent){
        int spanCount =-1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
          spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }

    /**
     * 判断当前item是不是最后一列
     * @param itemPosition
     * @param parent
     * @return
     */
    private boolean isLastColum(int itemPosition,RecyclerView parent){
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
            int spanCount = getSpanCount(parent);
            if(itemPosition+1%spanCount==0){
                return true;
            }
        }

        return false;
    }

    /**
     * 判断当前item是不是最后一行
     * @param itemPosition
     * @param parent
     * @return
     */
    private boolean isLastRow(int itemPosition,RecyclerView parent){
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
            int childCount = parent.getAdapter().getItemCount();//parent.getChildCount();这个不能用在这里,因为parent中的子个数在 getItemOffsets中是不断在变化的。依次加一。
            //思路一:
//            double currentRow = Math.ceil((double) (itemPosition + 1 )/ (double) spanCount);//当前行数
//            double rowCount = Math.ceil((double) childCount / (double) spanCount);//总行数
//            if(currentRow<rowCount){
//                return false;
//            }
            //思路二:
            childCount=childCount-childCount%spanCount;
            if((itemPosition+1)>=childCount){
                return true;
            }

        }

        return false;
    }

    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();

        Rect rect = new Rect();

        for (int i = 0; i <childCount ; i++) {
            View view = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();//使用这个是考虑到设置item中的背景间距的情况,在没有使用的时候这个部分可以删除。
            rect.left=view.getLeft()-layoutParams.leftMargin;
            rect.right=view.getRight()+layoutParams.rightMargin;
            rect.top=view.getBottom();
            rect.bottom=rect.top+mDrawable.getIntrinsicHeight();

            mDrawable.setBounds(rect);
            mDrawable.draw(c);


        }

    }

    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();

        Rect rect = new Rect();

        for (int i = 0; i <childCount ; i++) {
            View view = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();//使用这个是考虑到设置item中的背景间距的情况,在没有使用的时候这个部分可以删除。
            rect.left=view.getRight()+layoutParams.rightMargin;
            rect.right=rect.left+mDrawable.getIntrinsicWidth();
            rect.top=view.getTop()-layoutParams.topMargin-layoutParams.bottomMargin;
            rect.bottom=view.getBottom()+mDrawable.getIntrinsicHeight();//高是线的厚度;

            mDrawable.setBounds(rect);
            mDrawable.draw(c);


        }

    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if(isLastColum(parent.getChildLayoutPosition(view),parent)){
            outRect.set(0,0,mDrawable.getIntrinsicWidth(),0);
        }
        if(isLastRow(parent.getChildLayoutPosition(view),parent)){
            outRect.set(0,0,0,mDrawable.getIntrinsicHeight());
        }

    }
}

结果如下:


RecyclerViewbug_解决了右侧.jpg

但是我暂时还没有找到解决下面分割线的方法,如果有小哥指点一二,那就再好不过了。

第六步:实现RecyclerView的点击事件和长按事件。
写这个东西的思路就是;
1.首先先写个接口,(由于点击事件和长按事件的思路完全一样,我就写一个就行了。)

 /**
     * 点击事件接口
     */
    public interface OnItemClickListener{
        void onItemClick(int position);
    }

2.然后,在onBindViewHolder方法中利用itemView的点击事件,将点击事件所做的事传给之前定义的接口,

   //设置接口对象
    private OnItemClickListener onItemClickListener;

   //拿到控件
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onItemClickListener.onItemClick(position);
            }
        });

3.之后,在Adapter类中写一个设置点击事件的方法,setOnItemClickListener(OnItemClickListener itemClickListener)和

 public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener=onItemClickListener;

    }

4.最后,在Activity或Fragment中将具体事件的执行写到内部类中即可完成;

   mainClidRecyclerViewAdapter.setOnItemClickListener(new MainClidRecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                Toast.makeText(getActivity(),""+position,Toast.LENGTH_SHORT).show();
            }
        });

(如果需要设置某一个item中的控件的点击事件,思路还是如此,只需要在第二步把点击事件设置给item中的具体控件即可。)

写在最后

这个应该是我目前写得最长的一篇,不过写的过程很舒服,我还会再接再厉,加油!

参考:Android 图形基础类Rect,扎实基础助腾飞 - 王亟亟的博客 - CSDN博客 https://blog.csdn.net/ddwhan0123/article/details/53885732

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容