This is the second part in my article about how to create a custom view in Android.
Please follow these links for the other parts:
- Part I - Graphics
- Part II - Interaction
- Part III - Xml Attributes
Step 3 Change indicator position from the activity.
This step is far from the complexity in step 2, but still complex enough to warrant a few substeps.
Step 3.1 Setters and getters.
git checkout step_3_1
The requirement for this step was to be able to change the indicator position, but it makes sense to also make it possible to also alter the min and max values.
The getters are easy, but the setters need a few extras.
Let Eclipse do the easy part for you, by right-clicking on your class and selecting Source->Generate Getters and Setters. Tick mMin, mMax and mPosition and make sure to generate both getter and setter for mPosition and mMin. You don't need one for mMax, for reasons that will become clear in a moment.
After the code has been generated, you probably want to tidy up the method names slightly. Your getters should look something like this:
public float getMin() {As I said, the setters require a few extra lines. Since calling a setter will possibly alter a visible view, the view should be invalidated. But since invalidating views trigger a redrawing of it and possibly neighbouring views you should not invalidate unless needed. Furthermore you probably don't want the position to be outside of min and max, so you should check for that. Your setter for mPosition should thus look like this:
return mMin;
}
public float getMax() {
return mMax;
}
public float getPosition() {
return mPosition;
}
public void setPosition(float position) {You will also need to implement that helper method, within(). It should look something like this:
position = within(mMin, mMax);
if(position != mPosition) {
mPosition = position;
invalidate();
}
}
private float within(float position, final float min, final float max) {
if (position < min) {
position = min;
}
if (position > max) {
position = max;
}
return position;
}
Here is the reason for not generating a setter for mMax. For the slider to work properly, mMin must always be smaller than mMax, and the indicator must always be positioned somewhere in between min and max. That's why mMin and mMax must be set in the same method. The setter should check min and max, and throw an exception if they are wrong, and it should also adjust the position of the indicator. The adjustment can be done by reusing the setter for the position, like this (renaming setMin to setMinMax):
public void setMinMax(final float min, final float max) {
if ((min != mMin) || (max != mMax)) {
if (min > max) {
throw new IllegalArgumentException(
"setMinMax: min must be smaller than max.");
}
mMin = min;
mMax = max;
setPosition(mPosition);
invalidate();
}
}
Step 3.2 onPositionChanged
git checkout step_3_2
Being able to set and get the position is handy, but you will also need some way for the view to tell the activity that values have changed.
For this you will need a listener interface for your view and your activity needs to implement it in a usual manner. (The activity will be modified in the next step.)
Adding an listener interface is just a matter of specifying what should go in the interface and which object that should be tied to it.
First the interface. Put this into your class:
interface CustomSliderPositionListener {A new member field is also needed:
void onPositionChange(float newPosition);
}
private CustomSliderPositionListener mPositionListener;
The field will need a setter:
public void setPositionListener(final CustomSliderPositionListener listener) {
mPositionListener = listener;
}
And it should be initialised in the constructor, so you should add this line to the end of the constructor:
mPositionListener = null;
Finally, the listener should be called if the position does indeed changes, so you will need to add a few lines to setPosition, inside the if statement just after the call to invalidate:
if (mPositionListener != null) {
mPositionListener.onPositionChange(mPosition);
}
Ok, done.
(You should really add similar code for getting notified about changes to min and max as well. Feel free to do that, but I will not add it here, for the sake of the size of the tutorial.)
Step 3.3 Update the activity.
git checkout step_3_3
Again, the activity is there to show you that things are working, so let's do just that.
In you main.xml, add a text view and a button. The purpose of the text view is to show the current position of the indicator, and the purpose of the button is to reset it to the centre.
Your new main.xml should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.enea.training.customview.CustomSlider android:id="@+id/slider_horizontal"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
/>
<Button android:id="@+id/button_reset"
android:text="@string/reset"
android:onClick="onReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
<TextView android:id="@+id/values"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_above="@id/button_reset"
/>
</RelativeLayout>
Notice that I have changed the id of the slider, this is a hint of what is to come...
You will also need to add a string for your button. Add this to res/values/strings.xml:
<string name="reset">Reset</string>
You will also need to add a fair bit of code to your CustomSliderActivity. First of all you will need two member fields, for the text view and the slider:
private TextView mValueHorizontal;
private CustomSlider mSliderHorizontal;
You will also need to initialise th fields in onCreate. That includes implementing the onPositionChanged listener added in step 3.2, and setting min, max and position by calling the setters in step 3.1.
The listener calls to a helper method, displayValues() that will show the position of the indicator properly formatted in a text view.
Your new onCreate(), and the helper method, should look like this:
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mValues = (TextView) findViewById(R.id.values);
mSliderHorizontal = (CustomSlider) findViewById(R.id.slider_horizontal);
mSliderHorizontal.setPositionListener(new CustomSliderPositionListener() {
public void onPositionChange(final float newPosition) {
displayValues();
}
});
mSliderHorizontal.setMinMax(-100.0f, 100.0f);
mSliderHorizontal.setPosition(30.0f);
displayValues();
}
void displayValues() {
final String str = String.format("Horizontal: %3.2f",
mSliderHorizontal.getPosition());
mValues.setText(str);
}
All that is left now is to implement the callback for the button. Upon clicking the button, the indicator should go to the center of the slider. The code should look like this:
public void onReset(final View v) {Time for another test run of your project. Make sure the code works as expected, and don't forget to click the button.
final float min = mSliderHorizontal.getMin();
final float max = mSliderHorizontal.getMax();
final float newPos = (max - min) / 2 + min;
mSliderHorizontal.setPosition(newPos);
}
Modify the hard-coded values in onCreate and verify that the slider reflects your changes.
Step 4 onTouchListener
git checkout step_4
About time to interact a bit more. This is probably the simplest step of them all. All you need to do is in your constructor tell your view that it should use a touch listener:
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(final View v, final MotionEvent event) {
final float pos;
pos = (mMin + ((mMax - mMin) / (mIndicatorMaxPos - mIndicatorMinPos))
* event.getX());
setPosition(pos);
return true;
}
});
Done. Run your project and try to move the indicator with your finger. Hit the reset button to get it back to the center.
That's it for this part of the article. In the next part, you will see how to make a vertical slider, and how to set some attributes from xml.
- Part I - Graphics
- Part II - Interaction
- Part III - Xml Attributes
Please use the comments field if you have any questions.
/Robert