Android Custom/Compound Control

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.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.