Aneesh.A.M
Aneesh.A.M

Reputation: 1148

Linear Layout with Chat type Border

How can I provide a border with attached type for a Linear layout in Android. I am developing a Xamarin Android application.

enter image description here

I have given following code to achieve rounded corners for Layout.

<?xml version="1.0" encoding="UTF-8"?>

 <shape xmlns:android="http://schemas.android.com/apk/res/android" 
   android:shape="rectangle">
 <corners android:radius="10dp"/> 

 <padding android:left="10dp" 
  android:right="10dp" 
  android:top="10dp" 
  android:bottom="10dp"/>

 <stroke android:width="1dp" 
  android:color="#CCCCCC"/> </shape>

Upvotes: 1

Views: 176

Answers (2)

Leo Zhu
Leo Zhu

Reputation: 14956

as Seyed Masood Khademi said,you could use Nine Patch Image to achive the effect easily,ofcourse you could also do this with custom view.here is a simple sample:

create a custom view:

public  class BubbleLayout : FrameLayout
 {
    public static  int LEFT = 1;
    public static  int TOP = 2;
    public static  int RIGHT = 3;
    public static  int BOTTOM = 4;

    // radius size
    private int mRadius;


    //the center of the base of a triangle
    private Point mDatumPoint;

    //Triangle position offset (centered by default)
    private int mOffset;

    private Paint mBorderPaint;

    private Path mPath;

    private RectF mRect;

    //Direction of triangle
    private int mDirection;
    protected BubbleLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
    {

    }

    public BubbleLayout(Context context) : base(context)
    {
        init(context,null);
    }

    private void init(Context context, IAttributeSet attrs)
    {
        TypedArray ta = context.ObtainStyledAttributes(attrs, Resource.Styleable.BubbleLayout);
        //back ground color
        var backGroundColor = ta.GetColor(Resource.Styleable.BubbleLayout_background_color, Color.White);
        //shadow color
        var shadowColor = ta.GetColor(Resource.Styleable.BubbleLayout_shadow_color,
                Color.ParseColor("#999999"));
        int defShadowSize = (int)TypedValue.ApplyDimension(ComplexUnitType.Px,
                4, Resources.DisplayMetrics);
        //shadow size
        int shadowSize = ta.GetDimensionPixelSize(Resource.Styleable.BubbleLayout_shadow_size, defShadowSize);
        mRadius = ta.GetDimensionPixelSize(Resource.Styleable.BubbleLayout_radius, 0);
        //Triangular direction
        mDirection = ta.GetInt(Resource.Styleable.BubbleLayout_direction, BOTTOM);
        mOffset = ta.GetDimensionPixelOffset(Resource.Styleable.BubbleLayout_offset, 0);
        ta.Recycle();

        mBorderPaint = new Paint();
        mBorderPaint.AntiAlias = true;
        mBorderPaint.Color = backGroundColor;
        mBorderPaint.SetShadowLayer(shadowSize, 0, 0, shadowColor);

        mPath = new Path();
        mRect = new RectF();
        mDatumPoint = new Point();

        SetWillNotDraw(false);
        //Turn off hardware acceleration
        SetLayerType(LayerType.Software, null);
    }

    public BubbleLayout(Context context, IAttributeSet attrs) : base(context, attrs)
    {
        init(context, attrs); ;
    }

    public BubbleLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
    {
        init(context, attrs);
    }

    private void drawLeftTriangle(Canvas canvas)
    {
        int triangularLength = PaddingLeft;
        if (triangularLength == 0)
        {
            return;
        }

        mPath.AddRoundRect(mRect, mRadius, mRadius, Path.Direction.Ccw);
        mPath.MoveTo(mDatumPoint.X, mDatumPoint.Y - triangularLength / 2);
        mPath.LineTo(mDatumPoint.X - triangularLength / 2, mDatumPoint.Y);
        mPath.LineTo(mDatumPoint.X, mDatumPoint.Y + triangularLength / 2);
        mPath.Close();
        canvas.DrawPath(mPath, mBorderPaint);
    }

    private void drawTopTriangle(Canvas canvas)
    {
        int triangularLength = PaddingTop;
        if (triangularLength == 0)
        {
            return;
        }

        mPath.AddRoundRect(mRect, mRadius, mRadius, Path.Direction.Ccw);
        mPath.MoveTo(mDatumPoint.X + triangularLength / 2, mDatumPoint.Y);
        mPath.LineTo(mDatumPoint.X, mDatumPoint.Y - triangularLength / 2);
        mPath.LineTo(mDatumPoint.X - triangularLength / 2, mDatumPoint.Y);
        mPath.Close();
        canvas.DrawPath(mPath, mBorderPaint);
    }

    private void drawRightTriangle(Canvas canvas)
    {
        int triangularLength = PaddingRight;
        if (triangularLength == 0)
        {
            return;
        }

        mPath.AddRoundRect(mRect, mRadius, mRadius, Path.Direction.Ccw);
        mPath.MoveTo(mDatumPoint.X, mDatumPoint.Y - triangularLength / 2);
        mPath.LineTo(mDatumPoint.X + triangularLength / 2, mDatumPoint.Y);
        mPath.LineTo(mDatumPoint.X, mDatumPoint.Y + triangularLength / 2);
        mPath.Close();
        canvas.DrawPath(mPath, mBorderPaint);
    }

    private void drawBottomTriangle(Canvas canvas)
    {
        int triangularLength = PaddingBottom;
        if (triangularLength == 0)
        {
            return;
        }

        mPath.AddRoundRect(mRect, mRadius, mRadius, Path.Direction.Ccw);
        mPath.MoveTo(mDatumPoint.X + triangularLength / 2, mDatumPoint.Y);
        mPath.LineTo(mDatumPoint.X, mDatumPoint.Y + triangularLength / 2);
        mPath.LineTo(mDatumPoint.X - triangularLength / 2, mDatumPoint.Y);
        mPath.Close();
        canvas.DrawPath(mPath, mBorderPaint);
    }

    protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);
        if (mDatumPoint.X > 0 && mDatumPoint.Y > 0)
            switch (mDirection)
            {
                case 1:
                    drawLeftTriangle(canvas);
                    break;
                case 2:
                    drawTopTriangle(canvas);
                    break;
                case 3:
                    drawRightTriangle(canvas);
                    break;
                case 4:
                    drawBottomTriangle(canvas);
                    break;
            }
    }

    protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
    {
        base.OnSizeChanged(w, h, oldw, oldh);
        mRect.Left = PaddingLeft;
        mRect.Top = PaddingTop;
        mRect.Right = w - PaddingRight;
        mRect.Bottom = h - PaddingBottom;

        switch (mDirection)
        {
            case 1:
                mDatumPoint.X = PaddingLeft;
                mDatumPoint.Y = h / 2;
                break;
            case 2:
                mDatumPoint.X = w / 2;
                mDatumPoint.Y = PaddingTop;
                break;
            case 3:
                mDatumPoint.X = w - PaddingRight;
                mDatumPoint.Y = h / 2;
                break;
            case 4:
                mDatumPoint.X = w / 2;
                mDatumPoint.Y = h - PaddingBottom;
                break;
        }

        if (mOffset != 0)
        {
            applyOffset();
        }
    }


    /**
     * Set the triangle offset position
     *
     * @param offset 
    */
    public void setTriangleOffset(int offset)
    {
        this.mOffset = offset;
        applyOffset();
        Invalidate();
    }
    private void applyOffset()
    {
        switch (mDirection)
        {
            case 1:
            case 2:
                mDatumPoint.Y += mOffset;
                break;
            case 3:
            case 4:
                mDatumPoint.X += mOffset;
                break;
        }
    }
}  

define a attrs.xml in Resources/values:

<declare-styleable name="BubbleLayout">
  <attr name="background_color" format="color" />
  <attr name="shadow_color" format="color" />
  <attr name="shadow_size" format="dimension" />
  <attr name="radius" format="dimension" />
  <attr name="direction" format="enum">
    <enum name="left" value="1" />
    <enum name="top" value="2" />
    <enum name="right" value="3" />
    <enum name="bottom" value="4" />
</attr>
<attr name="offset" format="dimension" />

in your layout.xaml:

<namespace.BubbleLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="16dp"   //You must set enough padding to draw the triangle and shadow
    app:background_color="#FF4081"
    app:direction="bottom"
    app:offset="-20dp"
    app:radius="4dp"
    app:shadow_color="#999999"
    app:shadow_size="4dp">
    <TextView 
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="HelloWorld"
    />
</namespace.BubbleLayout>

Upvotes: 0

Seyed Masood Khademi
Seyed Masood Khademi

Reputation: 173

You must use Android Nine Patch Image. Nine patch image is an android special image format which can make the background image scaled correctly.

see this link

Upvotes: 1

Related Questions