Android Custom/Compound Control

Owais Idris
3 min readNov 15, 2016

--

When and why should I used?

In this article I am trying to make a custom view called IncrementView.
I want to make a compound control to capture hours and minutes.

  1. Native Time picker could be used to capture hours and minutes. Further in my app I want to capture height of a person in that case I will again need Feet/Inches picker.
  2. Configurable: Suppose I want a date picker now where hour part increments by 1 & minutes part increments by 10. This will make it easier for user to even input larger minutes. But for later cases there will be some other logic. I just cannot maintain code for different implementation and hard coded where needed. Hence I need something configurable
  3. Re-usability: As I am using same control for Hours and Minutes 2 times for same purpose there is a need of re-usability. 1 for hour, 1 for minutes similarly 1 for feet, 1 for inches etc.

This analysis made me think of a custom view.

e.g. of 2 Increment View.

Here is what I did
1. Created a layout.
2. Created a style-able.
3. Created a Subclass of View(LinearLayout in my case to get most out of it).

  1. Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>

<ImageButton
android:id="@+id/layout_increment_btnUp"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:srcCompat="@drawable/ic_arrow_drop_up_black_24dp"
/>

<TextView
android:id="@+id/layout_increment_txtValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="16dp"
android:background="@drawable/background_primary_circle"
android:padding="8dp"
android:text="0"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textColor="@android:color/white"
/>

<ImageButton
android:id="@+id/layout_increment_btnDown"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:srcCompat="@drawable/ic_arrow_drop_down_black_24dp"
/>

<TextView
android:id="@+id/layout_increment_txtLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="@color/colorAccent"
tools:text="Hours"
/>

</LinearLayout>

2.Create a attrs.xml file in /res/values

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="IncrementView">
<attr name="text" format="string" />
<attr name="step" format="integer" />
<attr name="default_value" format="integer" />
<attr name="maxLimit" format="integer" />
</declare-styleable>
</resources>
  • text: is label for control
  • step: increment value.
  • maxLimit: Top limit for counter.

3. Create Custom View Class which actually holds your logic and behavior.

ublic class IncrementView extends LinearLayout {

String mText = "";
int mStep = 1;
Context context;
TextView txtValue, txtLabel;
ImageButton btnUp, btnDown;
int mLimit = -1;

private int mValue = 0;// Actual value

public IncrementView(Context context) {
super(context);
this.context = context;
init();
}

public IncrementView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
mText = "";
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.IncrementView,
0, 0);

try {
mText = a.getString(R.styleable.IncrementView_text);
mStep = a.getInteger(R.styleable.IncrementView_step, 1);
mValue = a.getInteger(R.styleable.IncrementView_default_value, 0);
mLimit = a.getInteger(R.styleable.IncrementView_maxLimit, -1);
} finally {
a.recycle();
}
init();
}

private void init() {
View rootView = inflate(context, R.layout.layout_increment_view, this);
txtValue = (TextView) rootView.findViewById(R.id.layout_increment_txtValue);
txtLabel = (TextView) rootView.findViewById(R.id.layout_increment_txtLabel);
btnUp = (ImageButton) rootView.findViewById(R.id.layout_increment_btnUp);
btnDown = (ImageButton) rootView.findViewById(R.id.layout_increment_btnDown);
btnUp.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (mStep > 0) {
if (mLimit != -1) {
if (mValue + mStep <= mLimit) {
mValue += mStep;
}
} else {
mValue += mStep;
}
txtValue.setText("" + mValue);
invalidate();
}
}
});

btnDown.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (mStep > 0) {
mValue -= mStep;
if (mValue < 0) {
mValue = 0;
}
txtValue.setText("" + mValue);
invalidate();
}
}
});


txtValue.setText("" + mValue);
txtLabel.setText(mText);
if (mText.isEmpty()) {
txtLabel.setVisibility(INVISIBLE);
} else {
txtLabel.setVisibility(VISIBLE);
}
}
// for changing Value of Incrementer programatically
public int getValue() {
return mValue;
}
// Invalidate methods are crucial.
public void setValue(int value) {
mValue = value;
invalidate();
}


}

Usage

In my activity layout I did following

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.custom"
xmlns:tools="http://schemas.android.com/tools"
>
<!--Custom Control Used for Hour this must increase 1 at a time with Max hour 24-->
<com.dreamscapem.hourly.customViews.IncrementView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
custom:maxLimit="24"
custom:step="1"
custom:text="Hours"
/>

<!--Custom Control for Minute must increase 10 at a time and Max is 50-->
<com.dreamscapem.hourly.customViews.IncrementView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
custom:maxLimit="50"
custom:step="10"
custom:text="Minutes"
/>
</RelativeLayout>
  • Add a Name Space “custom”. Now my attributes defined in Style-able is available.
  • Added Controls.

I am happy to see my controls are just working out of the box. I will learn more and make this same control more beautiful and handy.

--

--

Owais Idris
Owais Idris

Written by Owais Idris

Into Android Development. Love for Automobile.

Responses (1)