Learn to create a Rotary Dialer Application for Android

  • A background image of the rotary dialer with numbers and letters. This image must be static during rotary dialer animation.
  • A top image with the tick. This image must be also static during animation.
  • Last image will be used to be rotated during the rotary dialer animation.
public class RotaryDialerView extends View {private final Drawable rotorDrawable;public RotaryDialerView(Context context) {
this(context, null);
}
public RotaryDialerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RotaryDialerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
rotorDrawable = context.getResources().getDrawable(R.drawable.dialer);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
rotorDrawable.setBounds(0, 0, rotorDrawable.getIntrinsicWidth(),
rotorDrawable.getIntrinsicHeight());
rotorDrawable.draw(canvas); canvas.restore();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<View
android:layout_width="320dip"
android:layout_height="320dip"
android:layout_centerInParent="true"
android:background="@drawable/bg" />
<com.ssaurel.rotarydialer.RotaryDialerView
android:id="@+id/rotary_dialer"
android:layout_width="320dip"
android:layout_height="320dip"
android:layout_centerInParent="true" />
<View
android:layout_width="320dip"
android:layout_height="320dip"
android:layout_centerInParent="true"
android:background="@drawable/top" />
</RelativeLayout>
</LinearLayout>
public class RotaryDialerView extends View {  private float rotorAngle;
private final Drawable rotorDrawable;
private final int r1 = 50;
private final int r2 = 160;
private double lastFi;
public RotaryDialerView(Context context) {
this(context, null);
}
public RotaryDialerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RotaryDialerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
rotorDrawable = context.getResources().getDrawable(R.drawable.dialer);
}
private void fireDialListenerEvent(int number) {
// TODO fire dial event
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int availableWidth = getRight() - getLeft();
int availableHeight = getBottom() - getTop();
int x = availableWidth / 2;
int y = availableHeight / 2;
canvas.save(); rotorDrawable.setBounds(0, 0, rotorDrawable.getIntrinsicWidth(),
rotorDrawable.getIntrinsicHeight());
if (rotorAngle != 0) {
canvas.rotate(rotorAngle, x, y);
}
rotorDrawable.draw(canvas); canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final float x0 = getWidth() / 2;
final float y0 = getHeight() / 2;
float x1 = event.getX();
float y1 = event.getY();
float x = x0 - x1;
float y = y0 - y1;
double r = Math.sqrt(x * x + y * y);
double sinfi = y / r;
double fi = Math.toDegrees(Math.asin(sinfi));
if (x1 > x0 && y0 > y1) {
fi = 180 - fi;
} else if (x1 > x0 && y1 > y0) {
fi = 180 - fi;
} else if (x0 > x1 && y1 > y0) {
fi += 360;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (r > r1 && r < r2) {
rotorAngle += Math.abs(fi - lastFi) + 0.25f;
rotorAngle %= 360;
lastFi = fi;
invalidate();
return true;
}
case MotionEvent.ACTION_DOWN:
rotorAngle = 0;
lastFi = fi;
return true;
case MotionEvent.ACTION_UP:
final float angle = rotorAngle % 360;
int number = Math.round(angle - 20) / 30;
if (number > 0) {
if (number == 10) {
number = 0;
}
fireDialListenerEvent(number);
}
rotorAngle = 0; post(new Runnable() {
public void run() {
float fromDegrees = angle;
Animation anim = new RotateAnimation(fromDegrees, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setInterpolator(AnimationUtils.loadInterpolator(getContext(), android.R.anim.decelerate_interpolator));
anim.setDuration((long) (angle * 5L));
startAnimation(anim);
}
});
return true;
default:
break;
}
return super.onTouchEvent(event);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="66dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:orientation="horizontal" >
<EditText
android:id="@+id/digits"
android:layout_width="0dip"
android:layout_height="66dp"
android:layout_weight="1"
android:background="@drawable/btn_dial_textfield"
android:focusableInTouchMode="false"
android:freezesText="true"
android:inputType="phone"
android:maxLines="1"
android:paddingLeft="45dip"
android:scrollHorizontally="true"
android:textColor="@color/dialer_button_text"
android:textSize="28sp" />
<ImageButton
android:id="@+id/backspace"
style="@android:style/Widget.Button.Inset"
android:layout_width="wrap_content"
android:layout_height="66dp"
android:background="@drawable/btn_dial_delete"
android:gravity="center"
android:src="@drawable/ic_delete_phone_number" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="18dp"
android:layout_marginTop="25dp"
android:src="@drawable/ic_dial_number" />
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<View
android:layout_width="320dip"
android:layout_height="320dip"
android:layout_centerInParent="true"
android:background="@drawable/bg" />
<com.ssaurel.rotarydialer.RotaryDialerView
android:id="@+id/rotary_dialer"
android:layout_width="320dip"
android:layout_height="320dip"
android:layout_centerInParent="true" />
<View
android:layout_width="320dip"
android:layout_height="320dip"
android:layout_centerInParent="true"
android:background="@drawable/top" />
</RelativeLayout>
</LinearLayout>
public interface DialListener {
void onDial(int number);
}
private final List<DialListener> dialListeners = new ArrayList<DialListener>();// ...public void addDialListener(DialListener listener) {
dialListeners.add(listener);
}
public void removeDialListener(DialListener listener) {
dialListeners.remove(listener);
}
private void fireDialListenerEvent(int number) {
for (DialListener listener : dialListeners) {
listener.onDial(number);
}
}
public class RotaryDialer extends Activity {
private EditText digits;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
RotaryDialerView rotaryDialerView = (RotaryDialerView) findViewById(R.id.rotary_dialer);
digits = (EditText) findViewById(R.id.digits);
rotaryDialerView.addDialListener(new RotaryDialerView.DialListener() {
public void onDial(int number) {
digits.append(String.valueOf(number));
}
});
ImageButton backspace = (ImageButton) findViewById(R.id.backspace);
backspace.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (digits.getText().toString().length() > 0) {
digits.getText().delete(digits.getText().length() - 1,
digits.getText().length());
}
}
});
backspace.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
if (digits.getText().toString().length() > 0) {
digits.getText().clear();
return true;
}
return false;
}
});
digits.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
makeCall();
}
});
} @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_CALL) {
makeCall();
return true;
}
return super.onKeyDown(keyCode, event);
}
private void makeCall() {
if (digits.getText().length() > 0) {
String toDial = "tel:" + digits.getText().toString();
startActivity(new Intent(Intent.ACTION_CALL, Uri.parse(toDial)));
}
}
}

--

--

Entrepreneur / Developer / Blogger / Author. In Bitcoin We Trust: https://inbitcoinwetrust.substack.com/

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sylvain Saurel

Sylvain Saurel

Entrepreneur / Developer / Blogger / Author. In Bitcoin We Trust: https://inbitcoinwetrust.substack.com/