Android Custom View: PlayStation Knob - Part 1: Getting Started

This is part 1 of the series "Android Custom View: PlayStation Knob", in which I will be creating an Android Custom View that acts just like the PlayStation Analogue Knob:


In this part, you will be able to:

  1. Create the Custom View
  2. Create Custom Attributes
  3. Use your View in an Activity
  4. Capture Touch Events to move the knob

Note: The code demonstrated here can be directly fetched from the git repository WidgyWidgets. You need to clone the repository and checkout to tags/knobview-part1 (Instruction are at the end of this post)

To start with this, you want to decide on whether to extend the base View class or to extend one of the widgets available in android.
In our case, I want to create a view that somehow shows in its center a circle that can be dragged by the user to any of the edges. One way to go would be to extend one of the available ViewGroups, add the knob in its center, and implement this dragging functionality. Another way would be to start the view from scratch by extending View.
To make things more interesting, I will, in this post, extend View. By the way, I will call it KnobView.

As a general practice I usually create a class that extends View and directly create a function called init and override the 3 constructors of View calling init() in all of them:

public class KnobView extends View {
 
 public KnobView(Context context) {
  super(context);
  init(context, null);
 }

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

 public KnobView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init(context, attrs);
 }

 private void init(Context context, AttributeSet attrs) {
  // TODO Auto-generated method stub
 }
}

If you do not know, the AttributeSet parameter in the constructor contains the attributes specified in the xml. To demonstrate, I will create an attribute called knob that will reference a drawable used for the knob in the center. To do so, we need to declare this attribute in the res folder. I will create a file called attr_knobview.xml in the res/values folder: this file will include the attributes of our KnobView. At this stage, we will only declare the attribute called knob of type integer since it is a reference to a drawable:
 
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="KnobView">
        <attr name="knob" format="integer" />
    </declare-styleable>
</resources>

Now in my init function I will get this drawable. (If you are working step by step, build your project after each change - e.g. NOW). To get the drawable:

private void init(Context context, AttributeSet attrs) {
 // TODO Auto-generated method stub
 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KnobView);
 int drawable = a.getResourceId(R.styleable.KnobView_knob, 0);
 if(drawable != 0) {
  mKnobDrawable = a.getDrawable(R.styleable.KnobView_knob);
 }
}

In this code, I try to get the integer which would be specified by the attribute knob (KnobView_knob). If this integer is not 0, I get the drawable and assign it to the field mKnobDrawable. You can add the field mKnobDrawable at the top of the class using this line: private Drawable mKnobDrawable;

What if there is no knob attribute specified in xml? I will simply create my own round black circle. This can be done in the else statement by creating a ShapeDrawable and assigning it to mKnobDrawable. Now my init function will assign a drawable to mKnobDrawable whether knob was specified or not.

private void init(Context context, AttributeSet attrs) {
 // TODO Auto-generated method stub
 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KnobView);
 int drawable = a.getResourceId(R.styleable.KnobView_knob, 0);
 if(drawable != 0) {
  mKnobDrawable = a.getDrawable(R.styleable.KnobView_knob);
 }
 else {
  mKnobDrawable = new ShapeDrawable(new OvalShape());
  ShapeDrawable s = (ShapeDrawable) mKnobDrawable;
         s.getPaint().setColor(Color.BLACK);
 }
}

Now we want to take care of the size of this knob. For now, I will center it in the view and let it take half the width and half the height. I will update the Bounds of this mKnobDrawable each time the size of our KnobView changes. This can be captured in onSizeChanged:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);
 mKnobDrawable.setBounds(w/2-w/4, h/2-h/4, w/2+w/4, h/2+h/4);
}

Notice that I am always updating the bounds of the knob drawable when the size changed. These bounds determine the actual rectangle in which my drawable will be drawn. I set the bounds to be the rectangle that is exactly half the size of the view and exactly located in the center of our view. It is pretty straight-forward.

Finally, I want to draw this Knob. This is simply calling the Drawable's draw function on our canvas in the onDraw function of the KnobView:

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 mKnobDrawable.draw(canvas);
}

For testing, I created an activity with the following xml layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/mobi.sherif.widgywidgetstest"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <mobi.sherif.widgywidgets.KnobView
        android:id="@+id/knob1"
        android:background="#f00"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="1" />
    <mobi.sherif.widgywidgets.KnobView
        android:id="@+id/knob2"
        android:background="#0f0"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:knob="@drawable/ic_launcher" />
</LinearLayout>

Notice:

  1. If you are doing your own project, you probably want to specify your package name instead of mobi.sherif.widgywidgetstest (second line)
  2. Due to layout_height="0dp" and layout_weight="1", each KnobView will take half the screen.
  3. The first KnobView does not specify the knob attribute while the second does.
  4. Each of the KnobView has a differnt background (red and blue).
Anyway, if you run this activity, you will get the output that is shown in the previous image.

With some modifications, int the drawables used for the background and the knob, I was able to get the following KnobView:

I modified the first KnobView in the layout (notice the background and the knob values)

    <mobi.sherif.widgywidgets.KnobView
        android:id="@+id/knob1"
        android:background="@drawable/bg_knobview"
        app:knob="@drawable/bg_knob"
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:layout_marginTop="100dip"
        android:layout_marginBottom="100dip" />

I also created res/bg_knobview
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" >
    <solid android:color="#aaaaaa" />
</shape>

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

Now it is time to move our knob. It is a very simple thing to: We will capture the touches on our view using the function onTouchEvent and when an ACTION_DOWN or ACTION_MOVE is detected, we move the knob to the location of the event. How do we do so? It's pretty easy: we use the setBounds function that we used in the onSizeChanged.

@Override
public boolean onTouchEvent(MotionEvent event) {
 final int action = MotionEventCompat.getActionMasked(event);
 if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
  int w = getWidth();
  int h = getHeight();
  int x = (int) event.getX();
  int y = (int) event.getY();
  mKnobDrawable.setBounds(x-w/4, y-h/4, x+w/4, y+h/4);
  invalidate();
 }
 return true;
}
Notice that we only did two things if the action is ACTION_DOWN or ACTION_MOVE:

  1. Set the bounds of our knob drawable based on the location of the event: We kept its width = w/2 and its height h/2 but we translated it to (x,y), the location of the event
  2. Invalidated the view using invalidate() to force our view to redraw -i.e. to move the knob.
The last natural thing to do is move the knob back when the user stops his touches. That is almost the same thing but with x and y set to the midpoint of the view -i.e. (w/2, h/2). Therefore our final onTouchEvent will look something like:

@Override
public boolean onTouchEvent(MotionEvent event) {
 final int action = MotionEventCompat.getActionMasked(event);
 if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
  int w = getWidth();
  int h = getHeight();
  int x = (int) event.getX();
  int y = (int) event.getY();
  mKnobDrawable.setBounds(x-w/4, y-h/4, x+w/4, y+h/4);
  invalidate();
 }
 else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
  int w = getWidth();
  int h = getHeight();
  int x = w/2;
  int y = h/2;
  mKnobDrawable.setBounds(x-w/4, y-h/4, x+w/4, y+h/4);
  invalidate();
    }
 return true;
}

Notice that the only difference is that x and y are not set to w/2 and h/2 respectively.

At the end of this part, your knob should be able to move when touched and return to its original place when released. Clone the WidgyWidget repository to try it yourself.

Have fun (:

Note: If you want to get the code of this part only, clone and checkout tags/knobview-part1 using the following commands (If you ommit folder_name, it will automatically be cloned into folder WidgyWidgets) :

git clone https://github.com/sherifelkhatib/WidgyWidgets.git folder_name
cd folder_name
git checkout tags/knobview-part1
... Try it and when you are done ...
git checkout master

30 comments:

  1. Thanks for sharing, nice post!

    - Với sự phát triển ngày càng tiến bộ của kỹ thuật công nghệ, nhiều sản phẩm thông minh ra đời với mục đích giúp cuộc sống chúng ta trở nên thoải mái và tiện lợi hơn. Và thiet bi dua vong tu dong ra đời là một trong những sản phẩm tinh túy của công nghệ, may ru vong tu dong là phương pháp ru con thời hiện đại của các ông bố bà mẹ bận rộn.

    - Là sản phẩm tuyệt vời của sự phát triển công nghệ, dung cu dua vong tu dong được thiết kế an toàn, tiện dụng. Những lợi ích mà may dua vong em be mang lại là vô cùng thiết thực.

    - Hiện nay trên thị trường có nhiều loại may dua vong cho em bé, sau nhiều năm kinh doanh và kinh nghiệm đút kết từ phản hồi của quý khách hàng sau khi mua máy, máy đưa võng tự động An Thái Sơn nhận thấy máy đưa võng tự động TS – sản phẩm may dua vong tu dong thiết kế dành riêng cho em bé, có chất lượng rất tốt, hoạt động êm, ổn định sức đưa đều, không giật cục, tuyệt đối an toàn cho trẻ, là lựa chọn hoàn hảo đảm bảo giấc ngủ ngon cho bé yêu của bạn.

    Bạn xem thêm bí quyết và chia sẽ kinh nghiệm làm đẹp:

    Những thực phẩm giúp đẹp da tại http://nhungthucphamgiupda.blogspot.com/
    Thực phẩm giúp bạn trẻ đẹp tại http://thucphamgiuptre.blogspot.com/
    Thực phẩm làm tăng tại http://thucphamlamtang.blogspot.com/
    Những thực phẩm giúp làm giảm tại http://thucphamlamgiam.blogspot.com/
    Những thực phẩm tốt cho tại http://thucphamtotcho.blogspot.com/

    Chúc các bạn vui vẻ!

    ReplyDelete
  2. In this world of ever growing technology, we can easily watch the latest movies or tv shows by streaming on different websites.
    Now we can even watch the movies and TV shows on our Android or Windows smartphones. One such app for watching movies is the PlayBox App.
    It's one of the best when it comes to watching movies or TV shows. To know more about the app and also get the apk link just visit my site
    PlayBox for iOS

    ReplyDelete
  3. I love to see you have written this content very Well and want to write more so people will aware of this issue
    I have some favorite game Blogs as well like
    ||Happy Wheels at happywheels.in||
    ||Happy wheels game at classic happy wheels game||
    ||Happy Wheels demo at happy wheels||
    ||Fireboy and watergirl at fireboywatergirl.co||

    ReplyDelete
  4. Thank you for posting such a great article! I found your website perfect for my needs
    visit our website

    ReplyDelete
  5. Great Article...Thanks for sharing the best information.It was so good to read and useful to improve my knowledge as updated one.

    Android Training

    ReplyDelete
  6. Interesting information and attractive.This blog is really rocking... Yes, the post is very interesting and I really like it.I never seen articles like this. I meant it's so knowledgeable, informative, and good looking site. I appreciate your hard work. Good job.
    Kindly visit us @
    Sathya Online Shopping
    Online AC Price | Air Conditioner Online | AC Offers Online | AC Online Shopping
    Inverter AC | Best Inverter AC | Inverter Split AC
    Buy Split AC Online | Best Split AC | Split AC Online
    LED TV Sale | Buy LED TV Online | Smart LED TV | LED TV Price
    Laptop Price | Laptops for Sale | Buy Laptop | Buy Laptop Online
    Full HD TV Price | LED HD TV Price
    Buy Ultra HD TV | Buy Ultra HD TV Online
    Buy Mobile Online | Buy Smartphone Online in India

    ReplyDelete
  7. The article is very interesting and very understood to be read, may be useful for the people. I wanted to thank you for this great read!! I definitely enjoyed every little bit of it. I have to bookmarked to check out new stuff on your post. Thanks for sharing the information keep updating, looking forward for more posts..
    Kindly visit us @
    Madurai Travels
    Best Travels in Madurai
    Cabs in Madurai
    Tours and Travels in Madurai

    ReplyDelete
  8. Excellent Blog. I really want to admire the quality of this post. I like the way of your presentation of ideas, views and valuable content. No doubt you are doing great work. I’ll be waiting for your next post. Thanks .Keep it up! Kindly visit us @
    Christmas Gift Boxes | Wallet Box
    Perfume Box Manufacturer | Candle Packaging Boxes | Luxury Leather Box | Luxury Clothes Box | Luxury Cosmetics Box
    Shoe Box Manufacturer | Luxury Watch Box

    ReplyDelete
  9. Wow, what an awesome spot to spend hours and hours! It's beautiful and I'm also surprised that you had it all to yourselves!
    Kindly visit us @ Best HIV Treatment in India | Top HIV Hospital in India | HIV AIDS Treatment in Mumbai | HIV Specialist in Bangalore
    HIV Positive Treatment in India | Medicine for AIDS in India

    ReplyDelete
  10. Thanks for updating this quality stuff. Treasurebox always provide you quality stuff for your home and garden.
    We will also provide you outdoor furniture with home delievery.

    ReplyDelete
  11. The article is very interesting and very understood to be read, may be useful for the people. I wanted to thank you for this great read!! I definitely enjoyed every little bit of it. I have to bookmarked to check out new stuff on your post. Thanks for sharing the information keep updating, looking forward for more posts..
    Kindly visit us @
    Madurai Travels | Travels in Madurai
    Best Travels in Madurai
    Cabs in Madurai | Madurai Cabs
    Tours and Travels in Madurai

    ReplyDelete
  12. Excellent Blog. I really want to admire the quality of this post. I like the way of your presentation of ideas, views and valuable content. No doubt you are doing great work. I’ll be waiting for your next post. Thanks .Keep it up!
    Kindly visit us @
    Luxury Packaging Box
    Wallet Box
    Perfume Box Manufacturer
    Candle Packaging Boxes
    Luxury Leather Box
    Luxury Clothes Box
    Luxury Cosmetics Box
    Shoe Box Manufacturer
    Luxury Watch Box

    ReplyDelete
  13. Attend The Data Science Courses in Bangalore From ExcelR. Practical Data Science Courses in Bangalore Sessions With Assured Placement Support From Experienced Faculty. ExcelR Offers The Data Science Courses in Bangalore.
    ExcelR Data Science Courses in Bangalore

    ReplyDelete
  14. Attend Business Analytics Training in Mumbai with 100% Syllabus Covered also Attend the Best Data Science Course in Mumbai. Faculty are From IIT & ISB. ExcelR is the Best Institute for Data Science Training in Mumbai

    Data Analytics Courses in Mumbai

    ReplyDelete
  15. Attend The PMP Certification From ExcelR. Practical PMP Certification Sessions With Assured Placement Support From Experienced Faculty. ExcelR Offers The PMP Certification.
    ExcelR PMP Certification

    ReplyDelete