Frage

My app has a background image of a music organ and I have used LinearLayouts with Buttons with their backgrounds set to @null to act as keys.

I've implemented my layout with View.OnTouchListener and in OnTouch I play the relevant sound on MotionEvent.ACTION_DOWN and stop it on MotionEvent.ACTION_UP.

This sort of works fine except that when I slide my fingers across the buttons (organ keys) due to them being buttons (I think) no new ontouch event are triggered unless I lift and retouch on a button.

Can anyone suggest a better layout that uses some other control perhaps that would enable me to detect a slide across them to achieve the slide up or down keyboard effect?

I don't really want to draw they keys from scratch as I want to utilise the graphics in the background image (for the organ keyboard) that is more pleasing than drawn rectangles. Thanks

    <LinearLayout android:id="@+id/ll" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/vibrato_off" android:orientation="vertical" android:weightSum="95" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context=".main">
<LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="70"></LinearLayout>
<LinearLayout android:id="@+id/llblack" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="11">
    <View android:layout_width="1dp" android:layout_height="0dip" android:layout_weight="1.5"/>
    <android.view.View android:id="@+id/vib_off" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <View android:layout_width="1dp" android:layout_height="0dip" android:layout_weight="0.4"/>
    <android.view.View android:id="@+id/a2u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/a2sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/b2u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/c3u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/c3sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/d3sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/e3u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/f3u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/f3sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/g3sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/a3sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/b3u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/c4u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <android.view.View android:id="@+id/c4sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/d4sharp" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/e4u" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="0.5" android:background="@null"/>
    <View android:layout_width="1dp" android:layout_height="0dip" android:layout_weight="0.5"/>
</LinearLayout>
<LinearLayout android:id="@+id/llwhite" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="9">
    <View android:layout_width="1dp" android:layout_height="0dip" android:layout_weight="1.5"/>
    <android.view.View android:id="@+id/vib_on" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <View android:layout_width="1dp" android:layout_height="0dip" android:layout_weight="0.4"/>
    <android.view.View android:id="@+id/a2" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/b2" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/c3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/d3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/e3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/f3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/g3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/a3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/b3" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/c4" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/d4" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <android.view.View android:id="@+id/e4" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" android:background="@null"/>
    <View android:layout_width="1dp" android:layout_height="0dip" android:layout_weight="0.4"/>
</LinearLayout>

War es hilfreich?

Lösung

I assume you're doing some math to know where to put the buttons so that they correspondent with the background image.

If you set an OnTouchListener to the whole LinearLayout and implement it to do stuff on ACTION_DOWN and ACTION_UP, you could use the same math to figure out where the user has touched the LinearLayout to know which key he or she actually intended to press. So you don't need buttons at all.

Then you would implement the listener to also do something on ACTION_MOVE and with the help of the math you're already doing, you could figure out when the user has left one key with his or her finger and moved to a different one. So you'd basically compare current coordinates of the finger to its coordinates when the onTouch() method was previously called, so you'd know to which key the movement went so that you can start playing its sound, and also from which key it went so you can stop playing the sound of the previous one. If the movement started and ended on the same key, you do nothing.

I did a similar thing about a year ago when I was implementing a calendar where the user could select an interval by sliding from one of the calendar cells to another. And the calendar had to update as the user was sliding, so I also had to know when he or she left one cell and moved the finger to another. So I am sure, that if you implement it correctly, it will work.

I assume, since it's supposed to be an app simulating a piano, that you need to allow the user to slide the keyboard not only with one, but with two (or more) fingers, right? I think this should also be easily possible because the MotionEvent carries information about the pointer (finger, mouse pointer, ...) that did the motion. I don't have any experience with that though, but you could read more about it at the MotionEvent documentation here: http://developer.android.com/reference/android/view/MotionEvent.html

Hope it helps. If you have further questions, I'd be happy to answer (also more concretely - I still have the calendar's code.)

Good luck.

EDIT to answer the comment:

I didn't mean using Views instead of Buttons. I meant your layout to consist only of a LinearLayout with the background image set to it. And that's it. Then in code, you attach an OnTouchListener to it and in the onTouch() method, you calculate the position. Off the top of my head, I think you could do it like this:

I assume the app should be used in a landscape mode and that the image of the piano covers the whole screen. Let’s say you have 8 white piano keys on it. By dividing the screen width by 8, you know how wide one piano key is on the device. You also know where one key starts and where it ends. Let’s say one key is 50 px wide. Then the first one starts on the 1st px and ends on the 50th. The second one starts on the 51st and ends on the 100th. And so on.

So then, if the touch event happens, you can access its coordinates by calling MotionEvent.getX or MotionEvent.getY. Now I don't know if you need getX or getY, you have to figure that out. So let's say that method returns a number like 94. When you divide it by your previously calculated piano key width, and round the result up to the nearest integer, you'll know which key the user pressed. So if the width is 50, you do 94 / 50 = 1.88, round it up to 2 and you have the 2nd key.

And you should store the getX/getY variable in your activity, so that when you detect the next event, you can tell what happened. So if you get an ACTION_DOWN on the 2nd key and then you get an ACTION_MOVE and you calculate it to actually already be on the 3rd key, you know the user has pressed the 2nd key and then slided to the 3rd key. So you stop playing 2nd key's sound and start playing the 3rd's one.

I hope it makes sense and that it's not a too primitive and stupid approach. :)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top