I am trying to create a schedule app where if you swipe left or right, the schedule for the next and previous day will be displayed.
The swiping works, however I want to implement a feature where if you double tap or long press anywhere on the screen in the MainActivity, it opens up another activity.
My problem is that I cannot implement the double tap/long press along with the swiping. (I tried removing the onDown method of my OnSwipeTouchListener and adding an OnLongPressListener, but what ended up happening was that a swipe would also trigger the long press. Any help would be appreciated!!
main.xml for MainActivity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ScrollView
android:id="#+id/scroller"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:layout_centerInParent="true" >
<LinearLayout
android:id="#+id/root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:animateLayoutChanges="false" >
</LinearLayout>
</ScrollView>
OnSwipeTouchListener.java
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class OnSwipeTouchListener implements OnTouchListener {
protected final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
result = true;
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
result = true;
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
// TODO Auto-generated method stub
return false;
}
}
Parts of MainActivity.java where I use OnSwipeTouchListener
// Setup stuff
LinearLayout layoutV = (LinearLayout) findViewById(R.id.root);
llV = layoutV;
ScrollView scroll = (ScrollView) findViewById(R.id.scroller);
scroller = scroll;
RelativeLayout rlayout = (RelativeLayout) findViewById(R.id.mainlayout);
rlayout.setOnTouchListener(new OnSwipeTouchListener(getApplicationContext()) {
public void onSwipeRight() {
switchDays(-1);
}
public void onSwipeLeft() {
switchDays(1);
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
scroller.setOnTouchListener(new OnSwipeTouchListener(getApplicationContext()) {
public void onSwipeRight() {
switchDays(-1);
}
public void onSwipeLeft() {
switchDays(1);
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
llV.setOnTouchListener(new OnSwipeTouchListener(getApplicationContext()) {
public void onSwipeRight() {
switchDays(-1);
}
public void onSwipeLeft() {
switchDays(1);
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
Related
I have an Adapter that extends RecyclerView.Adapter. Each line of adapter is a custom FrameLayout with GestureDetector implemented. The FrameLayout xml file has 2 RelativeLayout.
In this FrameLayout I redirect X and Y positions to the RelativeLayout that is in the foreground.
In each RelativeLayout I have a setOnClickListener. This is because if user clicks the RelativeLayout that is on the foreground, an activity is started. If the user scrolls the former, the latter is available to click.
What I'm facing is:
Implementing setOnClickListener on these layouts I lost view scroll feature of my custom FrameLayout. And I need the scroll feature and clickListener for each view.
This is my xml
file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/background_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/work_order_deleted_item_background_color">
...
</RelativeLayout>
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:visibility="invisible"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
...
</RelativeLayout>
</FrameLayout>
My custom FrameLayout:
public class SwipeView extends FrameLayout implements GestureDetector.OnGestureListener{
private RelativeLayout container;
private RelativeLayout backgroundContainer;
private GestureDetector detector;
private float posX;
private float posY;
private float lastTouchX;
private float lastTouchY;
private float xLimit;
private int mActivePointerId;
public SwipeView(#NonNull Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.adapter_work_order, this);
initViews(context, null);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
public SwipeView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initViews(context, attrs);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
public SwipeView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViews(context, attrs);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
private void initViews(Context context, AttributeSet attrs){
this.container = this.findViewById(R.id.container);
this.backgroundContainer = this.findViewById(R.id.background_container);
this.checkboxDownload = this.findViewById(R.id.checkbox_download);
this.textviewWo = this.findViewById(R.id.textview_wo);
this.downloadedWo = this.findViewById(R.id.textview_downloaded_wo);
this.textviewTime = this.findViewById(R.id.textview_time);
this.textviewField1 = this.findViewById(R.id.textview_field_1);
this.textviewField2 = this.findViewById(R.id.textview_field_2);
this.textviewField3 = this.findViewById(R.id.textview_field_3);
this.deleteLabel = this.backgroundContainer.findViewById(R.id.delete_wo);
}
#Override
public boolean onTouchEvent(MotionEvent event){
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Remember where we started (for dragging)
lastTouchX = x;
lastTouchY = y;
// Save the ID of this pointer (for dragging)
mActivePointerId = MotionEventCompat.getPointerId(event, 0);
if(x > container.getRight() + container.getX()){
Log.e("ERROR", "TOUCHED OUTSIDE");
}
else {
Log.e("ERROR", "TOUCHED INSIDE: ");
}
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Calculate the distance moved
final float dx = x - lastTouchX;
final float dy = y - lastTouchY;
posX += dx;
posY += dy;
if(posX <= -xLimit){
posX -= dx;
}
if(posX > 0.0f){
posX = 0.0f;
}
invalidate();
// Remember this touch position for the next move event
lastTouchX = x;
lastTouchY = y;
container.setX(posX);
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
}
return true;
}
#Override
public boolean onDown(MotionEvent motionEvent) {
return true;
}
#Override
public void onShowPress(MotionEvent motionEvent) {
}
#Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return true;
}
#Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return true;
}
#Override
public void onLongPress(MotionEvent motionEvent) {
}
#Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float velocityX, float velocityY) {
return true;
}
}
And this code is inside my adapter:
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//onItemClickListener.onItemClick(workOrder);
Log.e("ERROR", "FOREGROUND CLICKED");
}
});
holder.backgroundContainer.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
Log.e("ERROR", "BACKGROUND CLICKED");
}
});
Finally this is the code that is inside my Activity:
((MyAdapter) recyclerview.getAdapter())
.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
#Override
public void onItemClick(Item item) {
if(!presenter.getDownloadAvailability()) {
...
}
}
});
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:fillViewport="false">
<RelativeLayout
android:id="#+id/background_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/work_order_deleted_item_background_color">
...
</RelativeLayout>
</ScrollView>
I have the following code:
class ProductViewHolder extends RecyclerView.ViewHolder {
...
private Transition transition;
ProductViewHolder(CardViewEx view) {
super(view);
...
transition = TransitionInflater.from(recyclerView.getContext())
.inflateTransition(R.transition.price_button_transition);
transition.addTarget(R.id.button_price)
.addTarget(R.id.button_add_to_cart_or_customize)
.addTarget(R.id.view_price);
}
void changePriceButton(int titleRes) {
TransitionManager.beginDelayedTransition((ViewGroup) itemView, transition);
buttonPrice.setVisibility(View.GONE);
buttonAddToCartOrCustomize.setVisibility(View.VISIBLE);
buttonAddToCartOrCustomize.setText(titleRes);
}
void resetPriceButton(boolean withTransition) {
if (withTransition) {
TransitionManager.beginDelayedTransition((ViewGroup) itemView, transition);
}
buttonPrice.setVisibility(View.VISIBLE);
buttonAddToCartOrCustomize.setVisibility(View.GONE);
}
#OnClick(R.id.button_price)
void onPriceClicked() {
listener.onProductPriceClicked(product);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
resetPriceButton(false);
}
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
resetPriceButtons();
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
}
void resetPriceButtons() {
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
for (int i = firstVisibleItemPosition; i <= lastVisibleItemPosition; i++) {
ProductViewHolder holder = (ProductViewHolder) recyclerView.findViewHolderForAdapterPosition(i);
if (holder == null || holder.getLayoutPosition() != i) {
// inconsistency
notifyDataSetChanged();
return;
} else {
holder.resetPriceButton(true);
}
}
notifyItemRangeChanged(0, firstVisibleItemPosition);
notifyItemRangeChanged(lastVisibleItemPosition + 1, getItemCount() - (lastVisibleItemPosition + 1));
}
void changePriceButton(long productId, int titleRes) {
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
boolean found = false;
for (int i = firstVisibleItemPosition; i <= lastVisibleItemPosition; i++) {
ProductViewHolder holder = (ProductViewHolder) recyclerView.findViewHolderForAdapterPosition(i);
if (holder == null || holder.getLayoutPosition() != i) {
// inconsistency
notifyDataSetChanged();
return;
} else if (holder.product.getId() == productId) {
holder.changePriceButton(titleRes, true);
found = true;
}
}
if (!found) {
notifyItemRangeChanged(0, firstVisibleItemPosition);
notifyItemRangeChanged(lastVisibleItemPosition + 1, getItemCount() - (lastVisibleItemPosition + 1));
}
}
RecyclerView's item layout:
...
<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginBottom="8dp">
<ImageView android:id="#+id/image_product"
android:layout_width="64dp" android:layout_height="64dp"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"/>
<FrameLayout android:id="#+id/view_price"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp" android:layout_marginRight="8dp">
<Button style="#style/Button.Border.Green"
android:id="#+id/button_price"
android:minWidth="76dp"/>
<Button style="#style/Button.Flat.Green"
android:id="#+id/button_add_to_cart_or_customize"
android:layout_width="wrap_content"
android:minWidth="76dp"
android:minHeight="48dp"
android:textSize="13sp"/>
</FrameLayout>
<TextView android:id="#+id/text_product"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_toRightOf="#id/image_product" android:layout_toLeftOf="#id/view_price"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:textAppearance="#style/TextAllerRegular.13sp.Black87per"/>
</RelativeLayout>
I want to animate change price button to "Add to cart" when user clicks on the price, and back to the price when user starts scrolling RecyclerView.
When user clicks buttons and scroll recyclerview, button animations can stop in the middle state. You will see the margin between the right side of the window and the price button, or text "Add to cart" without the right padding on the button.
Then the button will be broken, you can try changePriceButton/resetPriceButton on it, but button won't show fully.
It is easy to reproduce this bug using "Animator duration scale" >= 5x.
It seems that TransitionManager has problems when you start beginDelayedTransition when there is beginDelayedTransition that already started on this view.
Also I think TransitionManager has problems with RecyclerView's view recycling on scrolling. Tried TransitionManager.endTransitions((ViewGroup) itemView) at the beginning of onBindViewHolder, but it didn't help.
The following solves the problem for me, but I am not sure fully.
Good to investigate the problem's reason.
I add the field isResetPriceButton in RecyclerView's holder for preventing attempts to change button to the same state when user starts scrolling RecycleView
private boolean isResetPriceButton;
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
isResetPriceButton = false;
...
resetPriceButton(false);
}
void changePriceButton(int titleRes) {
if (isResetPriceButton) {
isResetPriceButton = false;
TransitionManager.beginDelayedTransition((ViewGroup) itemView);
buttonPrice.setVisibility(View.GONE);
buttonAddToCartOrCustomize.setVisibility(View.VISIBLE);
buttonAddToCartOrCustomize.setText(titleRes);
}
}
void resetPriceButton(boolean withTransition) {
if (!isResetPriceButton) {
isResetPriceButton = true;
if (withTransition) {
TransitionUtils.beginDelayedTransitionIfPossible((ViewGroup) itemView);
}
buttonPrice.setVisibility(View.VISIBLE);
buttonAddToCartOrCustomize.setVisibility(View.GONE);
}
}
And I use the setHasTransientState on the holder's view during transition. But I don't like this, it produces extra views on scrolling when transitions have been started.
transition.addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
itemView.setHasTransientState(true);
}
#Override
public void onTransitionEnd(Transition transition) {
itemView.setHasTransientState(false);
}
#Override
public void onTransitionCancel(Transition transition) {
itemView.setHasTransientState(false);
}
#Override
public void onTransitionPause(Transition transition) {
}
#Override
public void onTransitionResume(Transition transition) {
}
});
activity_test_sliding_menu.xml
<com.hpc.cbs.myapplication.FlyOutContainer xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#444488"
android:orientation="vertical"
android:gravity="center">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toggleMenu"
android:text="Button 1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toggleMenu"
android:text="Button 1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toggleMenu"
android:text="Button 1"/>
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#888888"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="bla bla"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toggleMenu"
android:text="toggle"/>
</LinearLayout>
</com.hpc.cbs.myapplication.FlyOutContainer>
and here is class
FlyOutContainer.class
public class FlyOutContainer extends LinearLayout {
private View menu;
private View content;
//Constants
protected static final int menuMargin = 150;
protected enum MenuState {
CLOSED, OPEN, CLOSING, OPENING
};
//position information attributes
protected int currentContentOffset = 0;
protected MenuState menuCurrentState = MenuState.CLOSED;
//Animation objects
protected Scroller menuAnimationScroller = new Scroller(this.getContext(), new LinearInterpolator());
protected Runnable menuAnimationRunnable = new AnimationRunnable();
protected Handler menuAnimationHandler = new Handler();
//animation constants
private static final int menuAnimationDuration = 1000;
private static final int menuAnimationPollingInterval = 16;
public FlyOutContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public FlyOutContainer(Context context){
super(context);
}
protected void onAttachedToWindow(){
super.onAttachedToWindow();
this.menu = this.getChildAt(0);
this.content = this.getChildAt(1);
this.menu.setVisibility(View.GONE);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
if(changed)
this.calculateChildDimension();
this.menu.layout(left, top, right-menuMargin, bottom);
this.content.layout(left + this.currentContentOffset, top, right + this.currentContentOffset, bottom);
}
public void toggleMenu() {
switch (this.menuCurrentState) {
case CLOSED:
this.menu.setVisibility(View.VISIBLE);
this.currentContentOffset = this.getMenuWidth();
this.content.offsetLeftAndRight(currentContentOffset);
this.menuCurrentState = MenuState.OPEN;
break;
case OPEN:
this.menu.setVisibility(View.GONE);
this.currentContentOffset = 0;
this.content.offsetLeftAndRight(-currentContentOffset);
this.menuCurrentState = MenuState.CLOSED;
break;
default:
return;
}
this.menuAnimationHandler .postDelayed(this.menuAnimationRunnable, menuAnimationPollingInterval);
}
private int getMenuWidth() {
return this.menu.getLayoutParams().width;
}
private void calculateChildDimension(){
this.content.getLayoutParams().height = this.getHeight();
this.content.getLayoutParams().width = this.getWidth();
this.menu.getLayoutParams().width = this.getWidth() - menuMargin;
this.menu.getLayoutParams().height = this.getHeight();
}
private void adjustContentPosition(boolean isAnimationOngoing)
{
int scrollerOffset = this.menuAnimationScroller.getCurrX();
this.content.offsetLeftAndRight(scrollerOffset - this.currentContentOffset);
this.currentContentOffset = scrollerOffset;
this.invalidate();
if(isAnimationOngoing)
this.menuAnimationHandler.postDelayed(this.menuAnimationRunnable, menuAnimationPollingInterval);
else
this.onMenuTransitionComplete();
}
private void onMenuTransitionComplete()
{
switch (this.menuCurrentState)
{
case OPENING:
this.menuCurrentState = MenuState.OPEN;
break;
case CLOSING:
this.menuCurrentState = MenuState.CLOSED;
this.menu.setVisibility(View.GONE);
break;
default:
return;
}
}
protected class SmoothInterpolator implements Interpolator {
#Override
public float getInterpolation(float t)
{
return (float) Math.pow(t-1,5) + 1;
}
}
protected class AnimationRunnable implements Runnable
{
#Override
public void run()
{
boolean isAnimationOngoing = FlyOutContainer.this.menuAnimationScroller.computeScrollOffset();
FlyOutContainer.this.adjustContentPosition(isAnimationOngoing);
}
}
}
here is TestSlidingMenuActivity.java
public class TestSlidingMenuActivity extends Activity {
FlyOutContainer root;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.root = (FlyOutContainer) this.getLayoutInflater().inflate(R.layout.activity_test_sliding_menu, null);
setContentView(root);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.sample, menu);
return true;
}
public void toggleMenu(View v)
{
this.root.toggleMenu();
}
}
How can I fix this?
I create android app with notification.
Is exist any way for programatically determine user's gestures (horizontal and vertical swypes) on my notification?
Try using following code you can apply swiping gestures to ListView.
public class NotificationsFragment extends Fragment {
ListView mListView;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
mContext = getActivity();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// mOnTouchListener = new MyTouchListener();
view = inflater.inflate(R.layout.fragment_notification, container, false);
mListView = (ListView) view.findViewById(R.id.notification_list);
notificationtypeList = new ArrayList<String>();
mListView.setOnTouchListener(new OnSwipeTouchListener(mContext));
return view;
}
/**
* Detects left and right swipes across a view.
*/
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void onSwipeLeft() {
}
public void onSwipeRight() {
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float distanceX = e2.getX() - e1.getX();
float distanceY = e2.getY() - e1.getY();
int id = mListView.pointToPosition((int) e1.getX(), (int) e1.getY());
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD
&& Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (distanceX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
return true;
}
return false;
}
return listviewElementsheight;
}
}
So i'm making a xylophone app for android and i've run into a problem, i'm not sure how to correctly use onTouch. Right now I can press an image and play the sound, but I cannot play two sounds at once, nor can I slide my finger down the xylophone and play all the sounds, it will only play the first image pressed. I know this is because of onTouch and Multitouch but i'm not sure how to do this and I can't find any relevant sample code, any help is appreciated!
This is my main activity
public class Xylophone extends Activity {
private Player mSoundManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xylophone);
mSoundManager = new Player();
mSoundManager.initSounds(getBaseContext());
mSoundManager.addSound(1, R.raw.note01);
mSoundManager.addSound(2, R.raw.note02);
mSoundManager.addSound(3, R.raw.note03);
mSoundManager.addSound(4, R.raw.note04);
mSoundManager.addSound(5, R.raw.note05);
mSoundManager.addSound(6, R.raw.note06);
mSoundManager.addSound(7, R.raw.note07);
mSoundManager.addSound(8, R.raw.note08);
ImageView img01 = (ImageView) findViewById(R.id.imageView1);
img01.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mSoundManager.playSound(1);
return false;
}
});
ImageView img02 = (ImageView) findViewById(R.id.imageView2);
img02.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mSoundManager.playSound(2);
return false;
}
});
This is my SoundPool activity
public class Player {
private SoundPool mSoundPool;
private HashMap<Integer, Integer> mSoundPoolMap;
private AudioManager mAudioManager;
private Context mContext;
public Player()
{
}
public void initSounds(Context theContext) {
mContext = theContext;
mSoundPool = new SoundPool(8, AudioManager.STREAM_MUSIC, 0);
mSoundPoolMap = new HashMap<Integer, Integer>();
mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
}
public void addSound(int Index,int SoundID)
{
mSoundPoolMap.put(Index, mSoundPool.load(mContext, SoundID, 1));
}
public void playSound(int index) {
int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
}
public void playLoopedSound(int index) {
int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, -1, 1f);
}
}
This is my layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:orientation="horizontal" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:adjustViewBounds="true"
android:cropToPadding="false"
android:scaleType="fitXY"
android:src="#drawable/button01" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="fitXY"
android:src="#drawable/button02" />
hopefully you figured it out until now. Still here's my shot at it:
First of all your second class, SoundPool, is not an activity.
What you have to try is not overriding each button's onTouch method.
Instead, in your main activity, make it implement OnTouchListener.
Then of course, you need to implement/override the OnTouchListener's methods. So now, if you register your main view and/or every button(see for yourself which works!) to OnTouchListener, you will get all touches (multi touch as well).
Have a look at the MotionEvent class for more details
public class Xylophone extends Activity implements onTouchLister{
public static final String TAG = "Xylophone";
#Override
public void onCreate(Bundle savedInstanceState) {
/*Either have your parent view register THIS (Xylophone) as its listener, or have each button register THIS(Xylophone) as its listener.*/
yourParentView.setOnTouchListener(this);
//or
img02.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "ID:" + Integer.toString(v.getId()));
Log.i(TAG, "Pointer count: " + event.getPointerCount());
Log.i(TAG, "Action: " + event.getActionMasked());
Log.i(TAG, "Action index: " + event.getActionIndex());
}