1. Nimm jetzt an unserem 2. ADVENT-Gewinnspiel teil - Alle Informationen findest Du hier!

ListView Item Fling Gesture

Dieses Thema im Forum "Android App Entwicklung" wurde erstellt von MetBo, 26.06.2011.

  1. MetBo, 26.06.2011 #1
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Hallo,

    ich habe in meiner App eine ListView, in der ich gerne die "Fling Gesture" einbauen möchte. Dabei soll nicht die gesamte ListView angesprochen werden, sondern nur einzelne Einträge (wie in der Twitter-App, wenn man nach rechts oder links swipt).

    Um dies umzusetzen, habe ich mir gedacht, dass ich in jedem Eintrag ein ViewFlipper einbaue und über den Adapter die Gesten steuere.

    Code:
    package com.scibli.audio.recorder.utils;
    
    import java.util.ArrayList;
    
    import android.content.Context;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.GestureDetector.OnGestureListener;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnTouchListener;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.TextView;
    import android.widget.ViewFlipper;
    
    import com.scibli.audio.recorder.R;
    
    public class FileInfoAdapter extends BaseAdapter implements OnGestureListener, OnTouchListener {
    	protected static final CharSequence TAG = Util.getPackageName();
    	protected static final int SWIPE_MIN_DISTANCE = 120;
    	protected static final int SWIPE_MAX_OFF_PATH = 250;
    	protected static final int SWIPE_THRESHOLD_VELOCITY = 200;
    	
    	protected final Context mContext;
    	protected final LayoutInflater mLayoutInflater;
    	protected final GestureDetector mGestureDetector;
    	
    	protected ArrayList<FileInfo> mFileInfos;
    	
    	protected TextView mTextFileName;
    	protected TextView mTextDateTime;
    	protected TextView mTextFileSize;
    	protected TextView mTextDuration;
    	protected TextView mTextFileFormat;
    	protected ViewFlipper mFlipperEntry;
    	
    	public FileInfoAdapter(Context context, ArrayList<FileInfo> fileInfos) {
    		mContext = context;
    		mFileInfos = fileInfos;
    		mLayoutInflater = (LayoutInflater) mContext.getSystemService(
    				Context.LAYOUT_INFLATER_SERVICE);
    		mGestureDetector = new GestureDetector(this);
    	}
    
    	@Override
    	public int getCount() {
    		return mFileInfos.size();
    	}
    
    	@Override
    	public Object getItem(int arg0) {
    		return mFileInfos.get(arg0);
    	}
    
    	@Override
    	public long getItemId(int arg0) {
    		return 0;
    	}
    
    	@Override
    	public View getView(int arg0, View arg1, ViewGroup arg2) {
    		final View view = mLayoutInflater.inflate(R.layout.recorderlist_entry, null);
    		final FileInfo fileInfo = mFileInfos.get(arg0);
    		final CharSequence dateTime = android.text.format.DateFormat.getMediumDateFormat(mContext).format(
    				fileInfo.getDateTime()) + " " + android.text.format.DateFormat.getTimeFormat(mContext).format(
    						fileInfo.getDateTime());
    		
    		mTextFileName = (TextView) view.findViewById(R.id.textFileName);
    		mTextFileName.setText(fileInfo.getFileName());
    		
    		mTextDateTime = (TextView) view.findViewById(R.id.textDateTime);
    		mTextDateTime.setText(dateTime);
    		
    		mTextFileSize = (TextView) view.findViewById(R.id.textFileSize);
    		mTextFileSize.setText(fileInfo.getFileSize());
    		
    		mTextDuration = (TextView) view.findViewById(R.id.textDuration);
    		mTextDuration.setText(fileInfo.getDuration());
    		
    		mTextFileFormat = (TextView) view.findViewById(R.id.textFileFormat);
    		mTextFileFormat.setText(fileInfo.getFileFormat());
    		
    		mFlipperEntry =(ViewFlipper) view.findViewById(R.id.flipperEntry);
    		mFlipperEntry.setInAnimation(mContext, android.R.anim.fade_in);
    		mFlipperEntry.setOutAnimation(mContext, android.R.anim.fade_out);
    		
    		view.setOnTouchListener((OnTouchListener) this);
    		return view;
    	}
    
    	@Override
    	public boolean onDown(MotionEvent arg0) {
    		return true;
    	}
    
    	@Override
    	public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
    			float arg3) {
    		try {
                if(arg0.getX() > arg1.getX() && Math.abs(arg0.getX() - arg1.getX()) > SWIPE_MIN_DISTANCE 
                		&& Math.abs(arg2) > SWIPE_THRESHOLD_VELOCITY) {
                    mFlipperEntry.showNext();
                }else if (arg0.getX() < arg1.getX() && arg1.getX() - arg0.getX() > SWIPE_MIN_DISTANCE 
                		&& Math.abs(arg2) > SWIPE_THRESHOLD_VELOCITY) {
                	mFlipperEntry.showPrevious();
                }
            } catch (Exception e) {
                Log.e(TAG.toString(), e.getMessage());
            }
    		
    		return true;
    	}
    
    	@Override
    	public void onLongPress(MotionEvent arg0) {}
    
    	@Override
    	public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
    			float arg3) {
    		return true;
    	}
    
    	@Override
    	public void onShowPress(MotionEvent arg0) {}
    
    	@Override
    	public boolean onSingleTapUp(MotionEvent arg0) {
    		return true;
    	}
    
    	@Override
    	public boolean onTouch(View arg0, MotionEvent arg1) {
    		return mGestureDetector.onTouchEvent(arg1);
    	}
    
    }
    
    Problem dabei ist: Generell funktioniert dies ja, aber leider wird beim "flingen" immer der letzte Eintrag in der ListView angesprochen.

    Habt ihr eine Idee, wie ich jeden Eintrag einzeln ansprechen kann?
    Oder kennt ihr einen besseren Lösungsweg, wie ich dieses Feature von der Twitter-App nachbauen kann?

    Vielen Dank,
    Grüße
     
  2. MetBo, 27.06.2011 #2
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Push. Kennt keiner eine Lösung?
     
  3. MetBo, 01.07.2011 #3
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
  4. quercus, 01.07.2011 #4
    quercus

    quercus Gast

    Servus,

    wie hast du eigentlich die onFling() method und das amount of time we set gelöst. "Fling gesture" wird ja nur erkannt, so lange der Finger das Display berührt.
     
    Zuletzt von einem Moderator bearbeitet: 01.07.2011
  5. MetBo, 01.07.2011 #5
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Das ist die Frage. onFling wird durch
    erkannt bzw. initialisiert.

    Ich habe noch einen anderen Weg getestet: Den Gesturedetector der ListView zugewiesen. Dabei kann ich auch das entsprechende Item aus der Liste mit pointToPosition ansprechen. Nur leider reagiert der ViewFlipper dann nicht.
     
  6. quercus, 01.07.2011 #6
    quercus

    quercus Gast

    Stört es Dich wenn ich an deinem Code ein paar Versuche unternehme?

    PS das final float finde ich nicht
     
    Zuletzt von einem Moderator bearbeitet: 01.07.2011
  7. MetBo, 01.07.2011 #7
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Nein natürlich nicht. Was hast du denn vor? ;-)
     
  8. quercus, 01.07.2011 #8
    quercus

    quercus Gast

    Du hast da den onTouchListener anstatt den gestureListener?
     
  9. MetBo, 01.07.2011 #9
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Richtig, aber im Grunde ist der GestureListener ein onTouchListener ;-) Von daher ist es eigentlich egal, ob ich ihn implementiere, oder als Variable nutze.
     
  10. quercus, 01.07.2011 #10
    quercus

    quercus Gast

    ich nehm mal ein Beispiel für "Gesture" und "Listener":


    1. @Override
    2. public boolean onTouchEvent(MotionEvent event) {
    3. return gestures.onTouchEvent(event);
    4. }
    @Overridepublic boolean onTouchEvent(MotionEvent event) { return gestures.onTouchEvent(event);}
    und dann für das implementieren des Listeners:

    1. private class GestureListener implements GestureDetector.OnGestureListener,
    2. GestureDetector.OnDoubleTapListener {
    3. PlayAreaView view;
    4. public GestureListener(PlayAreaView view) {
    5. this.view = view;
    6. }
     
  11. MetBo, 01.07.2011 #11
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Meine Frage: Ist der GestureListener das richtige Instrument, um mein Problem zu lösen?
     
  12. quercus, 01.07.2011 #12
    quercus

    quercus Gast

    Ich denke schon - hoffe. :mellow:
     
  13. quercus, 01.07.2011 #13
    quercus

    quercus Gast

    Wennst die Lösung gefunden hast - schreibs hier rein, es interessiert mich!

    Danke!
     
  14. MetBo, 01.07.2011 #14
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Folgendes hatte ich eingebaut, hatte aber das gleiche Resultat wie vorher: nämlich keins!
     
  15. quercus, 01.07.2011 #15
    quercus

    quercus Gast

  16. MetBo, 01.07.2011 #16
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    Das Problem ist nicht das "Swipen" bzw "Flingen". Das funktioniert mit dem letzten Code von mir. Der ViewFlipper ist wohl das Problem ... Dieser reagiert nicht bei "showNext()". Aber gerne poste ich noch einmal meinen Code, damit du den aktuellen Code hast.
     
  17. quercus, 01.07.2011 #17
    quercus

    quercus Gast

    Ja gerne - somit ist das WE schon gerettet. :thumbup:
     
  18. MetBo, 01.07.2011 #18
    MetBo

    MetBo Threadstarter Fortgeschrittenes Mitglied

    Beiträge:
    264
    Erhaltene Danke:
    4
    Registriert seit:
    14.05.2010
    List Activity
    Code:
    public class ListActivity extends Activity  {
    	protected static final CharSequence TAG = Util.getPackageName();
    	protected static final CharSequence DIRECTORY = Util.getDirectory();
    	
    	protected static final int SWIPE_MIN_DISTANCE = 120;
    	protected static final int SWIPE_MAX_OFF_PATH = 250;
    	protected static final int SWIPE_THRESHOLD_VELOCITY = 200;
    	
    	protected ArrayList<FileInfo> mFileInfos;
    	protected Context mContext;
    	protected FileInfoAdapter mFileInfoAdapter;
    	
    	protected ListView mListRecords;
    	
    	protected GestureDetector mGestureDetector;
    	protected View.OnTouchListener mGestureListener;
    	
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recordlist);
            
            // Liste der Aufnahmen
            mListRecords = (ListView) findViewById(R.id.listRecords);
            
            mContext = getApplicationContext();
            	
    		// Alle Dateien aus dem Verzeichnis in der ListView anzeigen
    		mFileInfos = getFileArray();
    		
    		mFileInfoAdapter = new FileInfoAdapter(mContext, mFileInfos);
    		mListRecords.setAdapter(mFileInfoAdapter);
    		
    		mGestureDetector = new GestureDetector(new MyGestureDetector());
    		mGestureListener = new View.OnTouchListener() {
                public boolean onTouch(View v, MotionEvent aEvent) {
                	return mGestureDetector.onTouchEvent(aEvent);
                }
    		};
    		mListRecords.setOnTouchListener(mGestureListener);
    	}
    	
    	class MyGestureDetector extends SimpleOnGestureListener {
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                try {
                    if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
                    	return false;
                    }
    
                    int position = mListRecords.pointToPosition((int) Math.abs(e1.getX()), (int) Math.abs(e1.getY()));
                    ViewFlipper viewFlipper = mFileInfoAdapter.getViewFlipper(position);
                    
                    if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    	Toast.makeText(getApplicationContext(), "Right Swipe", Toast.LENGTH_SHORT).show();
                    	viewFlipper.showNext();
                    }  else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    	Toast.makeText(getApplicationContext(), "Left Swipe", Toast.LENGTH_SHORT).show();
                    	viewFlipper.showNext();
                    }
                } catch (Exception e) {
                    // nothing
                }
                return true;
            }
    	}
    }
    FileInfo Adapter
    Code:
    public class FileInfoAdapter extends BaseAdapter {
    	protected static final CharSequence TAG = Util.getPackageName();
    	protected static final int SWIPE_MIN_DISTANCE = 120;
    	
    	protected final Context mContext;
    	protected final LayoutInflater mLayoutInflater;
    	protected final ArrayList<SwipedViewFlipper> mSwipedViewFlipperList;
    	
    	protected ArrayList<FileInfo> mFileInfos;
    	
    	protected ViewFlipper mFlipperEntry;
    	
    	public FileInfoAdapter(Context context, ArrayList<FileInfo> fileInfos) {
    		mContext = context;
    		mFileInfos = fileInfos;
    		mLayoutInflater = (LayoutInflater) mContext.getSystemService(
    				Context.LAYOUT_INFLATER_SERVICE);
    		mSwipedViewFlipperList = new ArrayList<FileInfoAdapter.SwipedViewFlipper>();
    	}
    
    	@Override
    	public int getCount() {
    		return mFileInfos.size();
    	}
    
    	@Override
    	public Object getItem(int arg0) {
    		return mFileInfos.get(arg0);
    	}
    
    	@Override
    	public long getItemId(int arg0) {
    		return 0;
    	}
    
    	@Override
    	public View getView(int arg0, View arg1, ViewGroup arg2) {
    		final View view = mLayoutInflater.inflate(R.layout.recorderlist_entry, null);
    		final FileInfo fileInfo = mFileInfos.get(arg0);
    		
    		mFlipperEntry = (ViewFlipper) view.findViewById(R.id.flipperEntry);
    		
    		SwipedViewFlipper swipedViewFlipper = null;
    		try {
    			swipedViewFlipper = mSwipedViewFlipperList.get(arg0);
    		} catch (IndexOutOfBoundsException e) {
    			swipedViewFlipper = null;
    		}
    		
    		if (swipedViewFlipper == null) {
    			Log.e(TAG.toString(), "jo232");
    			mSwipedViewFlipperList.add(new SwipedViewFlipper(arg0, mFlipperEntry));
    		}
    		
    		return view;
    	}
    	
    	public ViewFlipper getViewFlipper(int position) {
    		try {
    			return mSwipedViewFlipperList.get(position).getViewFlipper();
    		} catch(IndexOutOfBoundsException e) {
    			return null;
    		}
    	}
    	
    	protected class SwipedViewFlipper {
    		protected int mPosition;
    		protected ViewFlipper mViewFlipper;
    		
    		public SwipedViewFlipper(int position, ViewFlipper viewFlipper) {
    			mPosition = position;
    			mViewFlipper = viewFlipper;
    		}
    		
    		public ViewFlipper getViewFlipper() {
    			return mViewFlipper;
    		}
    	}
    }
    
    Nun ... Wie kriege ich es nun hin, dass in der ListActivity der ViewFlipper mit showNext auf das "onFling" reagiert?
     
  19. quercus, 01.07.2011 #19
    quercus

    quercus Gast

    Na schau ma mal - ich melde mich im laufe des WEs!
     

Diese Seite empfehlen