Search code examples
androidandroid-layoutattributesandroid-custom-viewtimeline

Merge custom RelativeLayout with attributes in Android


I am using a RelativeLayout to create custom view. After couple days of trying I have no idea how to do what i want.

I need something like timeline. Line, circles that's all. I have created everything in one XML only to see everything in one place and it is working.

This is what I want to achieve:

what I want

and there is working XML timeline_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <RelativeLayout
        android:id="@+id/test1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true">

        <RelativeLayout
            android:id="@+id/draw1"
            android:layout_width="20dp"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/testText"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/testText"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp">

            <View
                android:id="@+id/lineUpDraw1"
                android:layout_width="1dp"
                android:layout_height="wrap_content"
                android:layout_above="@+id/circleDraw1"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true"
                android:layout_gravity="center"
                android:background="@color/primary"
                android:visibility="gone"/>

            <View
                android:id="@+id/circleDraw1"
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:background="@drawable/circle"/>

            <View
                android:id="@+id/lineDownDraw1"
                android:layout_width="1dp"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_below="@+id/circleDraw1"
                android:layout_centerHorizontal="true"
                android:layout_gravity="center"
                android:background="@color/primary"/>
        </RelativeLayout>

        <TextView
            android:id="@+id/testText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="4dp"
            android:layout_marginStart="4dp"
            android:layout_toEndOf="@+id/draw1"
            android:text="TEST1"
            android:textSize="16sp"/>

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/test2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/test1">

        <RelativeLayout
            android:id="@+id/draw2"
            android:layout_width="20dp"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/testText2"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/testText2"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp">

            <View
                android:id="@+id/lineUpDraw2"
                android:layout_width="1dp"
                android:layout_height="wrap_content"
                android:layout_above="@+id/circleDraw2"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true"
                android:layout_gravity="center"
                android:background="@color/primary"/>

            <View
                android:id="@+id/circleDraw2"
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:background="@drawable/circle"/>

            <View
                android:id="@+id/lineDownDraw2"
                android:layout_width="1dp"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_below="@+id/circleDraw2"
                android:layout_centerHorizontal="true"
                android:layout_gravity="center"
                android:background="@color/primary"/>
        </RelativeLayout>

        <TextView
            android:id="@+id/testText2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="4dp"
            android:layout_marginStart="4dp"
            android:layout_toEndOf="@+id/draw2"
            android:text="TEST2"
            android:textSize="16sp"/>

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/test3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/test2">

        <RelativeLayout
            android:id="@+id/draw3"
            android:layout_width="20dp"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/testText3"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/testText3"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp">

            <View
                android:id="@+id/lineUpDraw3"
                android:layout_width="1dp"
                android:layout_height="wrap_content"
                android:layout_above="@+id/circleDraw3"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true"
                android:layout_gravity="center"
                android:background="@color/primary"/>

            <View
                android:id="@+id/circleDraw3"
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:background="@drawable/circle"/>

            <View
                android:id="@+id/lineDownDraw3"
                android:layout_width="1dp"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_below="@+id/circleDraw3"
                android:layout_centerHorizontal="true"
                android:layout_gravity="center"
                android:background="@color/primary"
                android:visibility="gone"/>
        </RelativeLayout>

        <TextView
            android:id="@+id/testText3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="4dp"
            android:layout_marginStart="4dp"
            android:layout_toEndOf="@+id/draw3"
            android:text="TEST3"
            android:textSize="16sp"/>
    </RelativeLayout>

</RelativeLayout>

and drawable circle.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/primary"/>
</shape>

That working well but I want to get that generic with possibility to re-use this line-circle-line element. This is what I tried

timeline_element.xml

<?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">

    <View
        android:id="@+id/lineUp"
        android:layout_width="1dp"
        android:layout_height="wrap_content"
        android:layout_above="@+id/circle"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center"
        android:background="@color/primary"/>

    <View
        android:id="@+id/circle"
        android:layout_width="10dp"
        android:layout_height="10dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="@drawable/circle"/>

    <View
        android:id="@+id/lineDown"
        android:layout_width="1dp"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_below="@+id/circle"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center"
        android:background="@color/primary"/>

</RelativeLayout >

timeline

TimelineElement.java

package com.example;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import com.example.R;

public class TimelineElement extends RelativeLayout{

    public TimelineElement(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        View view = View.inflate(getContext(), R.layout.element_timeline, this);
        View lineUp = view.findViewById(R.id.lineUp);
        View circle = view.findViewById(R.id.circle);
        View lineDown = view.findViewById(R.id.lineDown);
        TypedArray a = getContext().getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.TimelineElement,
                0, 0);

        try {
            int overallWidth = a.getDimensionPixelSize(R.styleable.TimelineElement_overallWidth, 0);
            setLayoutParams(new LayoutParams(overallWidth, ViewGroup.LayoutParams.WRAP_CONTENT));

            int shape = a.getResourceId(R.styleable.TimelineElement_circle, R.drawable.circle);
            int circleSize = a.getDimensionPixelSize(R.styleable.TimelineElement_circleSize, 0);
            circle.setLayoutParams(new LayoutParams(circleSize, circleSize));
            circle.setBackground(getContext().getResources().getDrawable(shape));

            int lineColor = a.getColor(R.styleable.TimelineElement_lineColor, R.color.primary);
            lineUp.setBackgroundColor(lineColor);
            lineDown.setBackgroundColor(lineColor);

            int type = a.getInt(R.styleable.TimelineElement_linesType, 0);
            if(type == 1)
                lineDown.setVisibility(View.GONE);
            if(type == 2)
                lineUp.setVisibility(View.GONE);

        } finally {
            a.recycle();
        }
    }
}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TimelineElement">
        <attr name="lineColor" format="color" />
        <attr name="circle" format="reference" />
        <attr name="circleSize" format="dimension" />
        <attr name="overallWidth" format="dimension" />
        <attr name="linesType" format="enum">
            <enum name="both" value="0"/>
            <enum name="up" value="1"/>
            <enum name="down" value="2"/>
        </attr>
    </declare-styleable>
</resources>

timeline_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:timeline="http://schemas.android.com/apk/res-auto"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

    <RelativeLayout
        android:id="@+id/test1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true">

        <com.example.TimelineElement
            style="@style/TimelineElementStyle"
            android:id="@+id/draw1"
            android:layout_alignBottom="@+id/testText"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/testText"
            timeline:linesType="down"/>

        <TextView
            android:id="@+id/testText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="4dp"
            android:layout_marginStart="4dp"
            android:layout_toEndOf="@+id/draw1"
            android:text="TEST1"
            android:textSize="16sp"/>

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/test2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/test1">

        <com.example.TimelineElement
            style="@style/TimelineElementStyle"
            android:id="@+id/draw2"
            android:layout_alignBottom="@+id/testText2"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/testText2"/>

        <TextView
            android:id="@+id/testText2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="4dp"
            android:layout_marginStart="4dp"
            android:layout_toEndOf="@+id/draw2"
            android:text="TEST2"
            android:textSize="16sp"/>

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/test3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/test2">

        <com.example.TimelineElement
            style="@style/TimelineElementStyle"
            android:id="@+id/draw3"
            android:layout_alignBottom="@+id/testText3"
            android:layout_alignParentStart="true"
            android:layout_alignTop="@+id/testText3"
            timeline:linesType="up"/>

        <TextView
            android:id="@+id/testText3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="4dp"
            android:layout_marginStart="4dp"
            android:layout_toEndOf="@+id/draw3"
            android:text="TEST3"
            android:textSize="16sp"/>
    </RelativeLayout>

</RelativeLayout>

and styles.xml

<style name="TimelineElementStyle">
    <item name="android:layout_width">20dp</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:layout_marginEnd">8dp</item>
    <item name="android:layout_marginStart">8dp</item>
    <item name="circle">@drawable/circle</item>
    <item name="circleSize">10dp</item>
    <item name="lineColor">@color/primary</item>
    <item name="linesType">both</item>
</style>

Unfortunately result is not like I wish

result

I have no idea what can be wrong :( I want also use it in another context and that's why I really need to extract this line with circle without textview (it can be anything else in another context).

I hope you can help me! Thanks


Solution

  • My suggestion it's because here circle.setLayoutParams(new LayoutParams(circleSize, circleSize)); you define new layout params for the circle, and it loses its layout_centerHorizontal attribute.

    You can return it back programmatically: layoutparams.addRule(RelativeLayout.CENTER_HORIZONTAL);

    EDIT

    Another way:

    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) circle.getLayoutParams();
    params.height = ...;
    params.width = ...;
    circle.setLayoutParams(params);