牛X素材推荐之TriangleRectangleLabelView

来源:互联网 发布:淘客帝国源码 编辑:程序博客网 时间:2024/04/29 19:09

转载请注明出处:王亟亟的大牛之路

Git上看到一个比较给力的自定义展示标签的View,分享给大家TriangleRectangleLabelView。然后根据原作者的代码我会对其中运用的一些技术点加以解释,在此在此谢谢原作者的开源精神

先贴一下运行效果:
这里写图片描述

包结构
这里写图片描述

整体实现还是很轻量级的,只有一个实现的class和attrs来处理初始化。

TriangleRectangleLabelView(作者已详细标注)

public class TriangleRectangleLabelView extends TextView {    private static final String TAG = TriangleRectangleLabelView.class.getSimpleName();    private static final int DEFAULT_BG_COLOR = 0xff41c7cd;    private static final int DEFAULT_CIRCLE_COLOR = 0xffffffff;    private static final int DEFAULT_LINE_COLOR = 0xfffb9ece;    private static final int DEFAULT_HEIGHT = 30;//dp    private static final int DEFAULT_WIDTH = 70;//dp    private static final int DEFAULT_ROUND_RECT_RADIUS = 8;//px    private static final int DEFAULT_ROUND_RECT_WIDTH = 8;//px    private Paint mBgPaint;    private Paint mCirclePaint;    private Paint mLinePaint;    private int mRoundRectWidth = DEFAULT_ROUND_RECT_WIDTH;//圆角矩形宽度    private int mRoundRectRadius = DEFAULT_ROUND_RECT_RADIUS;//矩形圆角半径    private int mCircleRadius = 8;//圆点半径    private int mCircleSpaceRectangle = 16;//圆点到标签的间隔    private int mSpaceHeight = 8;//背景上下间隔    private int mLineWidth = 2;//竖线宽度    private int mBgColor = DEFAULT_BG_COLOR;//背景颜色    private int mLineColor = DEFAULT_LINE_COLOR;//竖直线颜色    private int mCircleColor = DEFAULT_CIRCLE_COLOR;//圆点颜色    private LINE_MODE mLineMode = LINE_MODE.MIDDLE;//竖线模式    private boolean isShowLine = false;//是否显示竖线    private boolean isShowCircle = true;//是否显示圆点    private boolean isDrawRoundRect = true;//是否显示矩形圆角    private boolean isLeft = true;//是否箭头朝左显示    private boolean isFirstPadding = true;    public static enum LINE_MODE {        START, MIDDLE, END    }    public TriangleRectangleLabelView(Context context) {        this(context, null);    }    public TriangleRectangleLabelView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public TriangleRectangleLabelView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initAttrs(attrs);        init();    }    private void initAttrs(AttributeSet attrs) {        if (attrs != null) {            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TriangleRectangleLabelView);            mBgColor = a.getColor(R.styleable.TriangleRectangleLabelView_trlvBgColor, mBgColor);            mLineColor = a.getColor(R.styleable.TriangleRectangleLabelView_trlvLineColor, mLineColor);            mCircleColor = a.getColor(R.styleable.TriangleRectangleLabelView_trlvCircleColor, mCircleColor);            isLeft = a.getBoolean(R.styleable.TriangleRectangleLabelView_trlvIsLeft, isLeft);            isShowLine = a.getBoolean(R.styleable.TriangleRectangleLabelView_trlvIsShowLine, isShowLine);            isShowCircle = a.getBoolean(R.styleable.TriangleRectangleLabelView_trlvIsShowCircle, isShowCircle);            isDrawRoundRect = a.getBoolean(R.styleable.TriangleRectangleLabelView_trlvIsDrawRoundRect, isDrawRoundRect);            mCircleRadius = a.getDimensionPixelSize(R.styleable.TriangleRectangleLabelView_trlvCircleRadius, mCircleRadius);            mCircleSpaceRectangle = a.getDimensionPixelSize(R.styleable.TriangleRectangleLabelView_trlvCircleSpaceRectangle, mCircleSpaceRectangle);            mLineWidth = a.getDimensionPixelSize(R.styleable.TriangleRectangleLabelView_trlvLineWidth, mLineWidth);            mRoundRectWidth = a.getDimensionPixelSize(R.styleable.TriangleRectangleLabelView_trlvRoundRectWidth, mRoundRectWidth);            mRoundRectRadius = a.getDimensionPixelSize(R.styleable.TriangleRectangleLabelView_trlvRoundRectRadius, mRoundRectRadius);            DisplayMetrics dm = getResources().getDisplayMetrics();            int minHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_HEIGHT, dm);            int minWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_WIDTH, dm);            setMinHeight(minHeight);            setMinWidth(minWidth);            a.recycle();        }    }    private void init() {        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mBgPaint.setStrokeWidth(4);        mBgPaint.setStyle(Paint.Style.FILL);        mBgPaint.setColor(mBgColor);        mBgPaint.setAntiAlias(true);        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mCirclePaint.setStrokeWidth(1);        mCirclePaint.setStyle(Paint.Style.FILL);        mCirclePaint.setColor(mCircleColor);        mCirclePaint.setAntiAlias(true);        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mLinePaint.setStrokeWidth(mLineWidth);        mLinePaint.setStyle(Paint.Style.FILL);        mLinePaint.setColor(mLineColor);        mLinePaint.setAntiAlias(true);    }    /**     * 设置圆角矩形宽度     *     * @param roundRectWidth px     */    public void setRoundRectWidth(int roundRectWidth) {        this.mRoundRectWidth = roundRectWidth;        postInvalidate();    }    /**     * 设置圆角矩形圆角半径     *     * @param roundRectRadius px     */    public void setRoundRectRadius(int roundRectRadius) {        this.mRoundRectRadius = roundRectRadius;        postInvalidate();    }    public void setBgColor(int color) {        mBgColor = color;        mBgPaint.setColor(mBgColor);        postInvalidate();    }    public void setLineColor(int color) {        mLineColor = color;        mLinePaint.setColor(getContext().getResources().getColor(mLineColor));        postInvalidate();    }    /**     * 设置竖直线模式     *     * @param lineMode LINE_MODE: START, MIDDLE, END     */    public void setLineMode(LINE_MODE lineMode) {        this.mLineMode = lineMode;        postInvalidate();    }    /**     * 是否箭头朝向左     *     * @param isLeft true:left false:right     */    public void setLeft(boolean isLeft) {        this.isLeft = isLeft;        postInvalidate();    }    /**     * 设置 View 上下间距     *     * @param spaceHeight px     */    public void setSpaceHeight(int spaceHeight) {        this.mSpaceHeight = spaceHeight;        isFirstPadding = true;        postInvalidate();    }    /**     * 设置原点与标签的间隔     *     * @param circleSpaceRectangle px     */    public void setCircleSpaceRectangle(int circleSpaceRectangle) {        this.mCircleSpaceRectangle = circleSpaceRectangle;        isFirstPadding = true;        postInvalidate();    }    /**     * 设置原点半径     *     * @param circleRadius px     */    public void setCircleRadius(int circleRadius) {        this.mCircleRadius = circleRadius;        isFirstPadding = true;        postInvalidate();    }    /**     * 设置分割线宽度     *     * @param lineWidth px     */    public void setLineWidth(int lineWidth) {        this.mLineWidth = lineWidth;        mLinePaint.setStrokeWidth(mLineWidth);        postInvalidate();    }    public void setShowLine(boolean isShowLine) {        this.isShowLine = isShowLine;        postInvalidate();    }    public void setShowCircle(boolean isShowCircle) {        this.isShowCircle = isShowCircle;        postInvalidate();    }    public void setDrawRoundRect(boolean isDrawRoundRect) {        this.isDrawRoundRect = isDrawRoundRect;        postInvalidate();    }    @Override    protected void onDraw(Canvas canvas) {        drawBackground(canvas);        super.onDraw(canvas);    }    private void drawBackground(Canvas canvas) {        int height = getHeight();        int width = getWidth();        Log.d(TAG, "height = " + height);        Log.d(TAG, "width = " + width);        Log.d(TAG, "isFirstPadding = " + isFirstPadding);        //-------------------Set Text Padding Begin----------------------------//        if (isFirstPadding) {            int paddingLeft = mCircleRadius * 2 + mCircleSpaceRectangle + (height - mSpaceHeight) / 2 + 2;            setPadding(isLeft ? paddingLeft : mRoundRectWidth, mSpaceHeight + 4, isLeft ? mRoundRectWidth + 4 : paddingLeft, mSpaceHeight + 4);            setGravity(Gravity.CENTER_VERTICAL);            isFirstPadding = false;        }        //-------------------Set Text Padding End----------------------------//        //-------------------Draw Line Begin----------------------------//        int startLineX = isShowLine ? (isLeft ? mCircleRadius : width - mCircleRadius) : 0;        if (isShowLine) {            if (mLineMode == LINE_MODE.START) {                canvas.drawLine(startLineX, height / 2, startLineX, height, mLinePaint);            } else if (mLineMode == LINE_MODE.MIDDLE) {                canvas.drawLine(startLineX, height, startLineX, 0, mLinePaint);            } else if (mLineMode == LINE_MODE.END) {                canvas.drawLine(startLineX, height / 2, startLineX, 0, mLinePaint);            }        }        //-------------------Draw Line End----------------------------//        //-------------------Draw Circle Begin----------------------------//        int startCircleX = isShowCircle ? (isLeft ? mCircleRadius : width - mCircleRadius) : 0;        if (isShowCircle) {            canvas.drawCircle(startCircleX, height / 2, mCircleRadius, mCirclePaint);        }        //-------------------Draw Circle End----------------------------//        //-------------------Draw RoundRect Begin----------------------------//        int roundLeft = isDrawRoundRect ? (isLeft ? width - mRoundRectWidth : 0) : 0;        int roundRight = isDrawRoundRect ? (isLeft ? width : mRoundRectWidth) : 0;        if (isDrawRoundRect) {            //draw RoundRect            RectF rect = new RectF();            rect.left = roundLeft;            rect.top = mSpaceHeight;            rect.right = roundRight;            rect.bottom = height - mSpaceHeight;            canvas.drawRoundRect(rect, mRoundRectRadius, mRoundRectRadius, mBgPaint);        }        //-------------------Draw RoundRect End----------------------------//        //-------------------Draw Triangle And Rectangle Begin----------------------------//        if (isLeft) {            //draw Triangle And Rectangle            int startX = mCircleSpaceRectangle + 2 * mCircleRadius;            Point a = new Point(startX, height / 2);            Point b = new Point(startX + (height - mSpaceHeight) / 2, height - mSpaceHeight);            Point c = new Point(width - (isDrawRoundRect ? mRoundRectWidth / 2 : 0), height - mSpaceHeight);            Point d = new Point(width - (isDrawRoundRect ? mRoundRectWidth / 2 : 0), mSpaceHeight);            Point e = new Point(startX + (height - mSpaceHeight) / 2, mSpaceHeight);            Point f = new Point(startX, height / 2);            Path path = new Path();            path.moveTo(a.x, a.y);            path.lineTo(b.x, b.y);            path.lineTo(c.x, c.y);            path.lineTo(d.x, d.y);            path.lineTo(e.x, e.y);            path.lineTo(f.x, f.y);            canvas.drawPath(path, mBgPaint);        } else {            //draw Triangle And Rectangle            int startX = width - (mCircleSpaceRectangle + 2 * mCircleRadius);            Point a = new Point(startX, height / 2);            Point b = new Point(startX - (height - mSpaceHeight) / 2, height - mSpaceHeight);            Point c = new Point((isDrawRoundRect ? mRoundRectWidth / 2 : 0), height - mSpaceHeight);            Point d = new Point((isDrawRoundRect ? mRoundRectWidth / 2 : 0), mSpaceHeight);            Point e = new Point(startX - (height - mSpaceHeight) / 2, mSpaceHeight);            Point f = new Point(startX, height / 2);            Path path = new Path();            path.moveTo(a.x, a.y);            path.lineTo(b.x, b.y);            path.lineTo(c.x, c.y);            path.lineTo(d.x, d.y);            path.lineTo(e.x, e.y);            path.lineTo(f.x, f.y);            canvas.drawPath(path, mBgPaint);        }        //-------------------Draw Triangle And Rectangle End----------------------------//    }}

分析:

如果要设置组合标签,就是都连在一起的需要做一下操作

 ((TriangleRectangleLabelView)findViewById(R.id.trlv1)).setLineMode(TriangleRectangleLabelView.LINE_MODE.START);        ((TriangleRectangleLabelView)findViewById(R.id.trlv2)).setLineMode(TriangleRectangleLabelView.LINE_MODE.MIDDLE);        ((TriangleRectangleLabelView)findViewById(R.id.trlv3)).setLineMode(TriangleRectangleLabelView.LINE_MODE.MIDDLE);        ((TriangleRectangleLabelView)findViewById(R.id.trlv4)).setLineMode(TriangleRectangleLabelView.LINE_MODE.END);

在initAttrs()方法内对标签内容进行了各种初始化,如果你的产品并不需要加以变化,可以写死,那还省蛮多代码的。

这个自定义View牛X的就是在drawBackground()方法中的画布和逻辑计算,写的还是相当缜密的。

重点提拎一下屏幕参数类DisplayMetics

这个类是干嘛的?

提供了一种关于显示的通用信息,如显示大小,分辨率和字体。

这个类怎么初始化?

DisplayMetrics metrics = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics;

而我们的自定义控件使用的是 DisplayMetrics dm = getResources().getDisplayMetrics();

补充下获取的参数是以像素为单位(Pixel) ,“像素”所指的是“绝对像素”而非“相对像素”。

通过 DisplayMetrics的 toString()方法可以获取到 DisplayMetrics的大部分 fields信息,如下是在分辨率为 480x320情况下的一些输出信息:

这里写图片描述

然后就是一系列的单位了
与分辨率无关的度量单位可以解决这一问题,Android支持下列所有单位:
px(像素):屏幕上的点。
in(英寸):长度单位。
mm(毫米):长度单位。
pt(磅):1/72英寸。
dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。
dip:与dp相同,多用于android/ophone示例中。
sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。

因为网络问题,也贴下别的 需要的文件,源码就不传了

主布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:paddingBottom="@dimen/activity_vertical_margin"    tools:context=".MainActivity"    android:orientation="vertical"    android:gravity="center_horizontal"    android:background="@drawable/bg">    <labelviewdemo.wjj.com.labelviewdemo.TriangleRectangleLabelView        android:id="@+id/trv_name"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="#fff"        android:singleLine="true"        android:text="Nice"        app:trlvIsLeft="false"        app:trlvCircleRadius="4dp"        android:textSize="16sp"        android:layout_marginLeft="48dp"        android:layout_marginTop="50dp"        android:layout_alignParentTop="true"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true" />    <labelviewdemo.wjj.com.labelviewdemo.TriangleRectangleLabelView        xmlns:app="http://schemas.android.com/apk/res-auto"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="#fff"        android:singleLine="true"        android:text="Label 1"        android:layout_below="@id/trv_name"        android:textSize="16sp"        android:layout_toEndOf="@id/trv_name"        android:layout_toRightOf="@id/trv_name"        app:trlvIsLeft="true"        app:trlvCircleRadius="4dp"        app:trlvBgColor="#ff000000"        app:trlvCircleColor="#ffffff"        app:trlvRoundRectRadius="12dp"        app:trlvRoundRectWidth="10dp" />    <labelviewdemo.wjj.com.labelviewdemo.TriangleRectangleLabelView        android:id="@+id/trlv1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="#fff"        android:singleLine="true"        android:text="标签1"        android:textSize="16sp"        android:layout_centerVertical="true"        android:layout_toEndOf="@id/trv_name"        android:layout_toRightOf="@id/trv_name"        app:trlvIsLeft="true"        app:trlvCircleRadius="4dp"        app:trlvBgColor="#72ddbf"        app:trlvCircleColor="#95e8cd"        app:trlvIsShowLine="true" />    <labelviewdemo.wjj.com.labelviewdemo.TriangleRectangleLabelView        android:id="@+id/trlv2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="#fff"        android:singleLine="true"        android:text="标签2"        android:textSize="16sp"        android:layout_below="@id/trlv1"        android:layout_toEndOf="@id/trv_name"        android:layout_toRightOf="@id/trv_name"        app:trlvIsLeft="true"        app:trlvCircleRadius="4dp"        app:trlvBgColor="#a596d6"        app:trlvCircleColor="#b6b2db"        app:trlvIsShowLine="true" />    <labelviewdemo.wjj.com.labelviewdemo.TriangleRectangleLabelView        android:id="@+id/trlv3"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="#fff"        android:singleLine="true"        android:text="标签3"        android:textSize="16sp"        android:layout_below="@id/trlv2"        android:layout_toEndOf="@id/trv_name"        android:layout_toRightOf="@id/trv_name"        app:trlvIsLeft="true"        app:trlvCircleRadius="4dp"        app:trlvBgColor="#efbe78"        app:trlvCircleColor="#f1cf9e"        app:trlvIsShowLine="true" />    <labelviewdemo.wjj.com.labelviewdemo.TriangleRectangleLabelView        android:id="@+id/trlv4"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:textColor="#fff"        android:singleLine="true"        android:text="标签4"        android:textSize="16sp"        android:layout_below="@id/trlv3"        android:layout_toEndOf="@id/trv_name"        android:layout_toRightOf="@id/trv_name"        app:trlvIsLeft="true"        app:trlvCircleRadius="4dp"        app:trlvBgColor="#f96bbc"        app:trlvCircleColor="#fc9dcf"        app:trlvIsShowLine="true" /></RelativeLayout>

MainActivity

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ((TriangleRectangleLabelView)findViewById(R.id.trlv1)).setLineMode(TriangleRectangleLabelView.LINE_MODE.START);        ((TriangleRectangleLabelView)findViewById(R.id.trlv2)).setLineMode(TriangleRectangleLabelView.LINE_MODE.MIDDLE);        ((TriangleRectangleLabelView)findViewById(R.id.trlv3)).setLineMode(TriangleRectangleLabelView.LINE_MODE.MIDDLE);        ((TriangleRectangleLabelView)findViewById(R.id.trlv4)).setLineMode(TriangleRectangleLabelView.LINE_MODE.END);    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }}

谢谢大家

2 0