andorid使用ItemDecoration绘制超炫酷标签

先上效果:
标签
左上角右上角的标签在开发中可能会用到,其实最初的时候是因为我一直都没有用ItemDecoration,个人不喜欢,太麻烦,还不如直接画出来,然后最下面一条隐藏底部分割线呢,用这东西看不到效果,调试时间会变长。但偶然间看了一篇博客,讲了除了这一用途外,可以用来做标签,这篇博客在:
https://www.jianshu.com/p/b46a4ff7c10a
此文章有截图如下:
此文章图如下
这个效果给了我一定的启发,ItemDecoration和adapter相比也有自己的优势,那就是入侵性低,既然这篇文说了可以用来做标签,那就做个例子试一下:
Activity代码:

public class DecorationActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_decoration);

        recyclerView = (RecyclerView) findViewById(R.id.recycler_decoration);

        initData();
        MyRecyclerAdapter recycleAdapter= new MyRecyclerAdapter(DecorationActivity.this , mDatas );
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //设置布局管理器
        recyclerView.setLayoutManager(layoutManager);
        //设置为垂直布局,这也是默认的
        layoutManager.setOrientation(OrientationHelper.VERTICAL);
        //设置Adapter
        recyclerView.setAdapter( recycleAdapter);
        //设置增加或删除条目的动画
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        //距离
//        recyclerView.addItemDecoration(new SimplePaddingDecoration(this));
        recyclerView.addItemDecoration(new SimpleDividerDecoration(this));
        recyclerView.addItemDecoration(new LeftAndRightTagDecoration(this));
//        recyclerView.addItemDecoration(new LinearLayoutColorDivider(getResources(),R.color.white,R.dimen.divider_height,LinearLayoutManager.VERTICAL));
    }

    private List mDatas;
    private void initData() {
        mDatas = new ArrayList<String>();
        for ( int i=0; i < 40; i++) {
            mDatas.add( "item"+i);
        }
    }
}

LeftAndRightTagDecoration:

public class LeftAndRightTagDecoration extends RecyclerView.ItemDecoration {
    private int tagWidth;
    private Paint leftPaint;
    private Paint rightPaint;
    private Paint textPaint;
    public LeftAndRightTagDecoration(Context context) {
        leftPaint = new Paint();
        leftPaint.setColor(context.getResources().getColor(R.color.colorAccent));
        rightPaint = new Paint();
        rightPaint.setColor(context.getResources().getColor(R.color.colorPrimary));
        textPaint = new Paint();
        textPaint.setColor(context.getResources().getColor(R.color.white));
        textPaint.setStrokeWidth(1);
        textPaint.setTextSize(20);
        textPaint.setTextAlign(Paint.Align.LEFT);
        tagWidth = context.getResources().getDimensionPixelSize(R.dimen.tag_width);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            int pos = parent.getChildAdapterPosition(child);
            boolean isLeft = pos % 2 == 0;
            if (isLeft) {
                drawLeftTriangle(c,child);
                drawLeftTag(c,child);

            } else {
                drawRightTriangle(c,child);
                drawRightTag(c,child);
            }
        }
    }

    private void drawRightTag(Canvas c, View child) {
        float top = child.getTop();
        String testString = "特价";

        Rect bounds = new Rect();
        textPaint.getTextBounds(testString, 0, testString.length(), bounds);

        c.save();
        c.translate(c.getWidth(),top);
        c.rotate(45);
        c.translate(-tagWidth/1.414f,0);

        float rectWidth = tagWidth*1.414f;
        float rectHeight = tagWidth/1.414f;
        float oldX = rectWidth/2 - bounds.width()/2;
        float oldY = rectHeight/2 + bounds.height()/2;
        c.drawText(testString, oldX, oldY, textPaint);
        c.restore();
    }

    private void drawRightTriangle(Canvas c, View child) {
        float right = child.getRight();
        float top = child.getTop();
        Path path= new Path();
        path.moveTo(right, top);
        path.lineTo(right-tagWidth, top);
        path.lineTo(right, tagWidth+top);
        path.close();
        c.drawPath(path, rightPaint);
    }

    private void drawLeftTag(Canvas c, View child) {
        float top = child.getTop();
        String testString = "特价";

        Rect bounds = new Rect();
        textPaint.getTextBounds(testString, 0, testString.length(), bounds);

        c.save();
        c.translate(0,top);
        c.rotate(-45);
        c.translate(-tagWidth/1.414f,0);

        float rectWidth = tagWidth*1.414f;
        float rectHeight = tagWidth/1.414f;
        float oldX = rectWidth/2 - bounds.width()/2;
        float oldY = rectHeight/2 + bounds.height()/2;
        c.drawText(testString, oldX, oldY, textPaint);
        c.restore();
    }

    private void drawLeftTriangle(Canvas c, View child) {
        float left = child.getLeft();
        float top = child.getTop();
        Path path= new Path();
        path.moveTo(left, top);
        path.lineTo(tagWidth+left, top);
        path.lineTo(left, tagWidth+top);
        path.close();
        c.drawPath(path, leftPaint);
    }
}

分割线的Decoration:

public class SimpleDividerDecoration extends RecyclerView.ItemDecoration {

    private int dividerHeight;
    private Paint dividerPaint;

    public SimpleDividerDecoration(Context context) {
        dividerPaint = new Paint();
        dividerPaint.setColor(context.getResources().getColor(R.color.black));
        dividerHeight = context.getResources().getDimensionPixelSize(R.dimen.divider_height);
    }


    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        int childCount = parent.getChildCount();
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        for (int i = 0; i < childCount-1; i++) {
            View view = parent.getChildAt(i);
            float top = view.getBottom();
            float bottom = view.getBottom() + dividerHeight;
            c.drawRect(left, top, right, bottom, dividerPaint);
        }
    }
}

代码很清楚,在左边的时候,就绘制左边的三角和左边的标签,右边的时候就绘制右边的三角,右边的标签。主要讲一下文字的绘制思路。

1.学会特定方形居中绘制:

String testString = "特价";
         Rect bounds = new Rect();
        textPaint.getTextBounds(testString, 0, testString.length(), bounds);
        loat oldX = rectWidth/2 - bounds.width()/2;
        float oldY = rectHeight/2 + bounds.height()/2;
        c.drawText(testString, oldX, oldY, textPaint);

这里的宽度是你要绘制的矩形在其内的宽度,这里的高度是你要绘制在其内的矩形高度
可以参考:下图中橙色区域是你要绘制的矩形
http://blog.csdn.net/lovexieyuan520/article/details/43153275

2.了解c.translate(a,b);c.rotate(c);c.save();c.restore();原理。

  • c.translate(a,b);
    这句表示移动坐标系到哪个位置,举个栗子:

这里写图片描述
打叉的地方坐标为(10,5),然后我们c.translate(10,5),坐标系移动到这里,变成:
这里写图片描述
坐标系平移到该位置,原来在(10,5)的点变成(0,0)

  • c.rotate(c);
    这句代表坐标系旋转角度
    这里写图片描述
    红色矩形为黑色矩形旋转c度得到,注意此处c.ratate(c);括号里面的c是旋转角度不是弧度。
    举个例子,原某点坐标为(3,3),c.rotate(45);后,此点新坐标为(0,3倍根号2)大约是(0,4.242)

c.save();
因为进行translate,rotate操作会改变坐标系,为了不给其他的点造成影响,需要使用c.save()在进行这些破坏坐标系操作前先保存旧的画布。
c.restore();
进行完对旋转平移坐标系的系列操作后,不继续使用了,则进行c.restore()恢复之前c.save()保存的坐标系。

3.标签文字绘制原理讲解

好了,下面讲解标签文字画法,以左边标签为例,右边同理:

 float top = child.getTop();
        String testString = "特价";

        Rect bounds = new Rect();
        textPaint.getTextBounds(testString, 0, testString.length(), bounds);

        c.save();
        c.translate(0,top);
        c.rotate(-45);
        c.translate(-tagWidth/1.414f,0);

        float rectWidth = tagWidth*1.414f;
        float rectHeight = tagWidth/1.414f;
        float oldX = rectWidth/2 - bounds.width()/2;
        float oldY = rectHeight/2 + bounds.height()/2;
        c.drawText(testString, oldX, oldY, textPaint);
        c.restore();

下面先看个图:
这里写图片描述
红色部分代表标签,ox2y2我期望移动到的新的坐标系。橙色加红色为字的居中长方形部分,我期望文字在此部分居中。
首先,需要把坐标移动到oxy的位置。因为除了第一行,其他行的坐标并非此位置,所以

c.translate(0,top);

假设标签的直角边为a。首先需要逆时针旋转45度使得新坐标ox1y1的x1横坐标与x2重合,y1纵坐标与y2平行,然后需要反向移动a/根号2(根据勾股定理),根号2约为1.414
这里写图片描述
于是:

c.rotate(-45);
c.translate(-tagWidth/1.414f,0);

然后注意了,标签的斜边为a*根号2,橙色的直角边为a/根号2,所以得到包含标签字的外部矩形长为a*根号2,宽为a/根号2.绘制即可。当然这样绘制看上去似乎比较靠近三角形的直角,可以通过修改rectHeight 来进行调整:

float rectWidth = tagWidth*1.414f;
        float rectHeight = tagWidth/1.414f;
        rectHeight = rectHeight/3*4;//将标签调整到rectHeight2/3的位置
        float oldX = rectWidth/2 - bounds.width()/2;
        float oldY = rectHeight/2 + bounds.height()/2;
        c.drawText(testString, oldX, oldY, textPaint);

过程是这样的,需要调整到矩形高度的2/3,那么可以扩大矩形为原来的4/3(居中则显示在2/3),调整一下字的大小,大约可以在32,可以显示出不错的效果:
这里写图片描述

添加阴影

上文中没提到阴影,因为到这行字以上,我的阴影还没调整好。
左标签加阴影:

    private int shadeWidth = 15;
    private void drawLeftTriangle(Canvas c, View child) {
        float left = child.getLeft();
        float top = child.getTop();
        Path path= new Path();
        path.moveTo(left, top);
        path.lineTo(tagWidth+left, top);
        path.lineTo(left, tagWidth+top);
        path.close();
        Shader mShader = new LinearGradient(left+tagWidth/2,top+tagWidth/2,left+(tagWidth+shadeWidth)/2,top+(tagWidth+shadeWidth)/2,new int[] {0x66000000,Color.TRANSPARENT},null,Shader.TileMode.CLAMP);

        Path path2= new Path();
        path2.moveTo(left, top);
        path2.lineTo(tagWidth+left+shadeWidth, top);
        path2.lineTo(left, tagWidth+top+shadeWidth);
        path2.close();
        leftPaint.setShader(mShader);
        c.drawPath(path2, leftPaint);
        leftPaint.setShader(null);
        c.drawPath(path, leftPaint);
    }

右标签加阴影:

 private void drawRightTriangle(Canvas c, View child) {
        float right = child.getRight();
        float top = child.getTop();
        Path path= new Path();
        path.moveTo(right, top);
        path.lineTo(right-tagWidth, top);
        path.lineTo(right, tagWidth+top);
        path.close();
        Shader mShader = new LinearGradient(right-tagWidth/2,top+tagWidth/2,right-(tagWidth+shadeWidth)/2,top+(tagWidth+shadeWidth)/2,new int[] {0x66000000,Color.TRANSPARENT},null,Shader.TileMode.CLAMP);

        Path path2= new Path();
        path2.moveTo(right, top);
        path2.lineTo(right-tagWidth-shadeWidth, top);
        path2.lineTo(right, tagWidth+shadeWidth+top);
        path2.close();
        rightPaint.setShader(mShader);
        c.drawPath(path2, rightPaint);
        rightPaint.setShader(null);
        c.drawPath(path, rightPaint);
    }

原理是这样的:先画阴影(使用渐变),再画标签,这样就呈现出阴影在下的效果。所以阴影要比标签要大一点,此处的阴影宽度不是真正的阴影宽度,真正阴影宽度为给出代码使用阴影宽度除以根号2.
这里写图片描述
如图,假设红色部分为标签部分,蓝色部分为阴影部分,红色部分宽度为a,蓝色部分横向宽度为b。渐变起点为A,终点为B,可得出渐变起点终点。当然,你也可以选用任何一条和A,B平行的有向线段做起点终点,比如A1(a,0),B1(a+b/2,b/2)也是可以的。
再次提醒,绘制阴影的范围要比标签大!当然,因为此处阴影只有一个方向,也可以选择绘制上图的蓝色梯形部分。
完成后如图所示:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值