(Android) 在 objects 的 collection 上实现滑动手势

(Android) implementing swipe gestures on a collection of objects

* 更新 -> 滑动手势现在可用(classes "PieceViewFragment" 和 "OnSwipeTouchListener" 已相应更新),但我真的很想得到从一个项目滑动到另一个项目时获得的漂亮视觉效果(运动)。现在它只是切换到下一个或上一个项目。


* 新更新 -> 我能够获得从一个项目滑动到另一个项目时获得的那种漂亮的视觉效果(移动)(感谢 Nobby_Nobody 的最后评论和 link).

在此处获取代码 –> My Project on github


我需要帮助来通过 Android 个人项目(清单列表)中的 collection 项目实现滑动手势。我已经看过很多例子,但我似乎无法正确实施它。

申请很简单。它只是一个清单 (InventairesPieces),实现为项目 (PieceModel) 的滚动列表(InventairesPiecesActivity 和 InventairesPiecesFragment),单击项目后,详细信息(PieceViewActivity 和 PieceViewFragment)将在另一个 activity.

中打开

在细节 activity(PieceViewActivity 和 PieceViewFragment)中,我希望能够在项目之间滑动,而不是每次都返回到滚动列表。

到目前为止,我尝试将“ViewPager”设置为在我的片段 (PieceViewFragment) 上扩展 FragmentStatePagerAdapter 的适配器,但它无法正常工作:

* 当点击一个项目时,它显示列表的第一个项目,无论点击哪个项目。操作栏中的标题也不匹配。 *

这是 "PieceViewActivity" class:

public class PieceViewActivity extends AppCompatActivity {

private InventairePieces inventairePieces;

private PieceModel piece;

private int position;

/**
 * The pager widget, which handles animation and allows swiping horizontally to access previous
 * and next wizard steps.
 */
private ViewPager mViewPager;

/**
 * The pager adapter, which provides the pages to the view pager widget.
 */
private InventairePiecesPagerAdapter inventairePiecesPagerAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_piece_view);

    // receives the intent
    Intent intent = this.getIntent();
    inventairePieces = (InventairePieces) intent.getSerializableExtra("inventairePieces");
    position = (int) intent.getSerializableExtra("posClicked");

    // Instantiate a ViewPager and a PagerAdapter.
    inventairePiecesPagerAdapter = new InventairePiecesPagerAdapter(getSupportFragmentManager());
    mViewPager = (ViewPager) findViewById(R.id.activity_piece_view_container);
    mViewPager.setAdapter(inventairePiecesPagerAdapter);
}

public class InventairePiecesPagerAdapter extends FragmentStatePagerAdapter {

    public InventairePiecesPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int posView) {
        return PieceViewFragment.create(posView, position, inventairePieces);
    }

    @Override
    //the amount of items in the inventory
    public int getCount() {
        return inventairePieces.getInventairePieces().size();
    }

}

}

这里是"PieceViewFragment"class(详情或卡片):

public class PieceViewFragment extends Fragment {

protected OnSwipeTouchListener onSwipeTouchListener;

private InventairePieces inventairePieces;

private PieceModel piece;

private int positionClicked;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    final View rootView = inflater.inflate(R.layout.fragment_piece_view, null, false);

    // receives the intent
    Intent intent = getActivity().getIntent();
    inventairePieces = (InventairePieces) intent.getSerializableExtra("inventairePieces");
    positionClicked = (int) intent.getSerializableExtra("posClicked");
    piece = inventairePieces.getInventairePieces().get(positionClicked);

    // sets the title in the action bar
    getActivity().setTitle(piece.getNomPiece());

    Log.v("Fragment", "Pos Clicked: " + positionClicked);
    Log.v("Fragment" , "Piece: " + piece.getNomPiece());
    Log.v("Fragment", "Inventaire: " + inventairePieces.getInventairePieces().size());

    // the item card
    ((TextView) rootView.findViewById(R.id.codePiece_text)).setText(String.valueOf("# " + piece.getCodePiece()));
    ((TextView) rootView.findViewById(R.id.nomPiece_text)).setText(piece.getNomPiece());
    ((TextView) rootView.findViewById(R.id.descriptionPiece_text)).setText(piece.getDescriptionPiece());
    ((TextView) rootView.findViewById(R.id.dimensionPiece_text)).setText(String.valueOf(piece.getDimensionPiece()) + " mm");
    ((TextView) rootView.findViewById(R.id.prixCoutantPiece_text)).setText(String.valueOf(piece.getPrixCoutantPiece()) + " $");
    ((TextView) rootView.findViewById(R.id.qtyPiece_text)).setText(String.valueOf(piece.getQtyPiece()));
    ((TextView) rootView.findViewById(R.id.typePiece_text)).setText(piece.getTypePiece());
    ((TextView) rootView.findViewById(R.id.categoriePiece_text)).setText(piece.getCategoriePiece());

    rootView.setOnTouchListener(new OnSwipeTouchListener(getContext()){
        @Override
        public void onSwipeRight() {
            super.onSwipeRight();
            Log.v("Fragment", "onSwipeRight - ");
            if (positionClicked > 0) {
                positionClicked--;
                piece = inventairePieces.getInventairePieces().get(positionClicked);

                // sets the title in the action bar
                getActivity().setTitle(piece.getNomPiece());

                // the item card
                ((TextView) rootView.findViewById(R.id.codePiece_text)).setText(String.valueOf("# " + piece.getCodePiece()));
                ((TextView) rootView.findViewById(R.id.nomPiece_text)).setText(piece.getNomPiece());
                ((TextView) rootView.findViewById(R.id.descriptionPiece_text)).setText(piece.getDescriptionPiece());
                ((TextView) rootView.findViewById(R.id.dimensionPiece_text)).setText(String.valueOf(piece.getDimensionPiece()) + " mm");
                ((TextView) rootView.findViewById(R.id.prixCoutantPiece_text)).setText(String.valueOf(piece.getPrixCoutantPiece()) + " $");
                ((TextView) rootView.findViewById(R.id.qtyPiece_text)).setText(String.valueOf(piece.getQtyPiece()));
                ((TextView) rootView.findViewById(R.id.typePiece_text)).setText(piece.getTypePiece());
                ((TextView) rootView.findViewById(R.id.categoriePiece_text)).setText(piece.getCategoriePiece());

                Log.v("Fragment", "Pos Clicked: " + positionClicked);
                Log.v("Fragment" , "Piece: " + piece.getNomPiece());
                Log.v("Fragment", "Inventaire: " + inventairePieces.getInventairePieces().size());
            }
        }
        @Override
        public void onSwipeLeft() {
            super.onSwipeLeft();
            Log.v("Fragment", "onSwipeLeft + ");
            if(positionClicked < inventairePieces.getInventairePieces().size()-1) {
                positionClicked++;
                piece = inventairePieces.getInventairePieces().get(positionClicked);

                // sets the title in the action bar
                getActivity().setTitle(piece.getNomPiece());

                // the item card
                ((TextView) rootView.findViewById(R.id.codePiece_text)).setText(String.valueOf("# " + piece.getCodePiece()));
                ((TextView) rootView.findViewById(R.id.nomPiece_text)).setText(piece.getNomPiece());
                ((TextView) rootView.findViewById(R.id.descriptionPiece_text)).setText(piece.getDescriptionPiece());
                ((TextView) rootView.findViewById(R.id.dimensionPiece_text)).setText(String.valueOf(piece.getDimensionPiece()) + " mm");
                ((TextView) rootView.findViewById(R.id.prixCoutantPiece_text)).setText(String.valueOf(piece.getPrixCoutantPiece()) + " $");
                ((TextView) rootView.findViewById(R.id.qtyPiece_text)).setText(String.valueOf(piece.getQtyPiece()));
                ((TextView) rootView.findViewById(R.id.typePiece_text)).setText(piece.getTypePiece());
                ((TextView) rootView.findViewById(R.id.categoriePiece_text)).setText(piece.getCategoriePiece());

                Log.v("Fragment", "Pos Clicked: " + positionClicked);
                Log.v("Fragment" , "Piece: " + piece.getNomPiece());
                Log.v("Fragment", "Inventaire: " + inventairePieces.getInventairePieces().size());
            }
        }
    });

    return rootView;
}

}

这里是处理手势的新 OnSwipeTouchListener class:

public class OnSwipeTouchListener implements View.OnTouchListener {

private GestureDetector gestureDetector;

public OnSwipeTouchListener(Context c) {
    gestureDetector = new GestureDetector(c, new GestureListener());
}

public boolean onTouch(final View view, final MotionEvent motionEvent) {
    return gestureDetector.onTouchEvent(motionEvent);
}

private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    // Determines the fling velocity and then fires the appropriate swipe event accordingly
    @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();
                    }
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeDown();
                    } else {
                        onSwipeUp();
                    }
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}

public void onSwipeRight() {
}

public void onSwipeLeft() {
}

public void onSwipeUp() {
}

public void onSwipeDown() {
}

}

这里是 "InventairesPiecesFragment" class 您在滚动列表中单击某个项目的位置:

public class InventairePiecesFragment extends Fragment{

private ArrayAdapter<PieceModel> inventairePiecesAdapter;

private InventairePieces inventairePieces;

private PieceModel piece;

private Bundle bundle;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    FloatingActionButton fab = (FloatingActionButton) container.findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(getActivity(), PieceEditActivity.class);
            intent.putExtra("piece", piece);
            intent.putExtra("inventairePieces", inventairePieces);
            startActivityForResult(intent, 1);
        }
    });

    inventairePieces = new InventairePieces(new ArrayList<PieceModel>());
    this.readInventairePiece();

    inventairePiecesAdapter = new ArrayAdapter<>(
            getActivity(),
            R.layout.liste_pieces_inventaire,
            R.id.liste_pieces_inventaire_textview,
            inventairePieces.getInventairePieces());

    View rootView = inflater.inflate(R.layout.fragment_pieces_inventaire, container, false);

    ListView inventairePiecesAdapterView = (ListView) rootView.findViewById(R.id.listview_pieces_inventaire);
    inventairePiecesAdapterView.setAdapter(inventairePiecesAdapter);

    inventairePiecesAdapterView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            piece = inventairePiecesAdapter.getItem(i);
            Intent intent = new Intent(getActivity(), PieceViewActivity.class);
            intent.putExtra("inventairePieces", inventairePieces);
            intent.putExtra("posClicked", i);
            startActivityForResult(intent, 1);

            Log.v("short clicked","pos: " + i);
        }
    });
    inventairePiecesAdapterView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int i, long id) {
            piece = inventairePiecesAdapter.getItem(i);
            ConfirmeSuppDialogFragment dialogFrag = new ConfirmeSuppDialogFragment();
            bundle = new Bundle();
            bundle.putSerializable("piece",piece);
            bundle.putSerializable("inventairePieces",inventairePieces);
            dialogFrag.setArguments(bundle);
            dialogFrag.setTargetFragment(InventairePiecesFragment.this, 2);
            dialogFrag.show(getFragmentManager(), "dialog");

            Log.v("long clicked","pos: " + i);

            return true;
        }
    });


    return rootView;
}



@Override
public boolean onOptionsItemSelected(MenuItem item) {
    this.writeInventairePiece();
    return super.onOptionsItemSelected(item);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(resultCode == 1) {
        piece = (PieceModel) data.getSerializableExtra("piece");
        inventairePieces.addToInventairePieces(piece);
        this.writeInventairePiece();
        inventairePiecesAdapter.notifyDataSetChanged();
        printConfirmerState(piece, "ajoutée");
    }
    else if(resultCode == 2){
        inventairePieces.removeFromInventairePieces(piece);
        this.writeInventairePiece();
        inventairePiecesAdapter.notifyDataSetChanged();
        printConfirmerState(piece, "supprimée");
    }
}

private void writeInventairePiece(){
    try {
        FileOutputStream outputFile = this.getContext().openFileOutput("InventairePiece.ser", Context.MODE_PRIVATE);
        ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
        outputStream.writeObject(inventairePieces);
        outputStream.close();
        outputFile.close();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

private void readInventairePiece(){
    try {
        FileInputStream inputFile = this.getContext().openFileInput("InventairePiece.ser");
        ObjectInputStream inputStream = new ObjectInputStream(inputFile);
        inventairePieces = (InventairePieces) inputStream.readObject();
        inputStream.close();
        inputFile.close();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

private void printConfirmerState(PieceModel piece, String state){
    String confirm = state;
    for(int i = 0; i < inventairePieces.getInventairePieces().size(); i++) {
        if(i == inventairePieces.getInventairePieces().indexOf(piece) && state.equals("ajoutée")) {
            confirm = ("La pièce '" + piece.getNomPiece() + "' est " + state + ".");
        }else if(i == inventairePieces.getInventairePieces().indexOf(piece) && state.equals("supprimée")) {
            confirm = ("La pièce '" + piece.getNomPiece() + "' est " + state + ".");
        }
    }
    Toast.makeText(getContext(), confirm, Toast.LENGTH_SHORT).show();
}


/*@Override
public void updateResult(int result) {
    this.result = result;
}*/

}

先谢谢你了。

为了以防万一,这是我目前为止的完整申请: My Project on gitHub

让我看看我是否做对了,你有项目清单吗?每当您 select 任何项目时,您都会显示 Activity 以及该项目的所有详细信息 (PieceModel);并且您想在此片段中实现手势识别器以在上一个和下一个项目之间切换而无需返回列表并且 select 新项目紧张?

如果我理解的不错,你可以这样做:

  1. 实现手势识别器来处理片段上的左右滑动。
  2. 根据手势,您还可以在创建列表适配器时从正在使用的数据源获取新项目对象 (PieceModel),并更新存储在片段中的当前实例。
  3. 使用此 "new" 项更新 UI,就好像该片段是从头开始加载的一样。

瞧!!!,这里有一些链接:

https://developer.android.com/training/gestures/detector.html

http://guides.codepath.com/android/gestures-and-touch-events

顺便说一句,当您将 selected 项的索引传递给新的 Activity 时,您应该将其检索为:

position = getIntent().getIntExtra("posClicked");

我强烈建议您传递项目 Id 而不是位置,顺便说一下,InventairePieces 是否实现了 Serializable 接口?

我还能够使用 ViewPager 和内部 class 扩展 FragmentStatePagerAdapter 实现滑动手势。

最终并没有那么复杂,但一开始我真的很难让它工作,这就是为什么我也分享了那个代码,希望它最终能帮助到别人。在此处获取代码 –> My Project on github

以下是受这些更改影响的两个 class:

PieceViewActivity:

public class PieceViewActivity extends AppCompatActivity {
    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide fragments representing
     * each object in a collection. We use a {@link android.support.v4.app.FragmentStatePagerAdapter}
     * derivative, which will destroy and re-create fragments as needed, saving and restoring their
     * state in the process. This is important to conserve memory and is a best practice when
     * allowing navigation between objects in a potentially large collection.
     */
    PieceViewPagerAdapter pieceViewPagerAdapter;

    /**
     * The {@link android.support.v4.view.ViewPager} that will display the object collection.
     */
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_piece_view);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.activity_piece_view_container, new PieceViewFragment())
                    .commit();

            // Create an adapter that when requested, will return a fragment representing an object in
            // the collection.
            pieceViewPagerAdapter = new PieceViewPagerAdapter(getSupportFragmentManager());
            // Set up the ViewPager, attaching the adapter.
            mViewPager = (ViewPager) findViewById(R.id.activity_piece_view_container);
            mViewPager.setAdapter(pieceViewPagerAdapter);

            //Receives the position of the item peviously clicked in the inventory list
            Intent intent = getIntent();
            mViewPager.setCurrentItem(intent.getIntExtra("posClicked", -1));
        }
    }

    /**
     * A {@link android.support.v4.app.FragmentStatePagerAdapter} that returns a fragment
     * representing an object in the collection.
     */
    public class PieceViewPagerAdapter extends FragmentStatePagerAdapter {

        //Accesses the InventairePieces class
        private InventairePieces inventairePieces;

        public PieceViewPagerAdapter(FragmentManager fm) {
            super(fm);

            Intent intent = getIntent();
            inventairePieces = (InventairePieces) intent.getSerializableExtra("inventairePieces");
        }

        /**
         * Gets the item position on swipe gestures
         * sends the detail fragment in a bundle
         * @param position the item position from swipe gesture
         * @return the new fragment details
         */
        @Override
        public Fragment getItem(int position) {
            Fragment fragment = new PieceViewFragment();
            Bundle args = new Bundle();
            args.putInt("position", position);
            fragment.setArguments(args);
            return fragment;
        }

        /**
         * Gets the size of the inventory from an intent
         * @return the size of the inventory
         */
        @Override
        public int getCount() {
            return inventairePieces.getInventairePieces().size();
        }
    }
}

PieveViewFragment:

public class PieceViewFragment extends Fragment {

    //Accesses the InventairePieces class
    private InventairePieces inventairePieces;
    //Accesses the PieceModel class
    private PieceModel piece;
    //The object's position in the inventory list
    private int positionClicked;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);

        //Gets the inventory from an intent
        Intent intent = getActivity().getIntent();
        positionClicked = intent.getIntExtra("posClicked", -1);
        inventairePieces = (InventairePieces) intent.getSerializableExtra("inventairePieces");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        final View rootView = inflater.inflate(R.layout.fragment_piece_view, container, false);

        //Gets the item position from a bundle
        Bundle args = getArguments();

        if(args != null) {
            positionClicked = args.getInt("position");
            piece = inventairePieces.getInventairePieces().get(positionClicked);

            //Sets the object's details
            ((TextView) rootView.findViewById(R.id.invCount_text)).setText(String.valueOf("(" + (positionClicked + 1) + "/" + inventairePieces.getInventairePieces().size()) + ")");
            ((TextView) rootView.findViewById(R.id.codePiece_text)).setText(String.valueOf("# " + piece.getCodePiece()));
            ((TextView) rootView.findViewById(R.id.nomPiece_text)).setText(piece.getNomPiece());
            ((TextView) rootView.findViewById(R.id.descriptionPiece_text)).setText(piece.getDescriptionPiece());
            ((TextView) rootView.findViewById(R.id.dimensionPiece_text)).setText(String.valueOf(piece.getDimensionPiece()) + " mm");
            ((TextView) rootView.findViewById(R.id.prixCoutantPiece_text)).setText(String.valueOf(piece.getPrixCoutantPiece()) + " $");
            ((TextView) rootView.findViewById(R.id.qtyPiece_text)).setText(String.valueOf(piece.getQtyPiece()));
            ((TextView) rootView.findViewById(R.id.typePiece_text)).setText(piece.getTypePiece());
            ((TextView) rootView.findViewById(R.id.categoriePiece_text)).setText(piece.getCategoriePiece());
        }
        return rootView;
    }
}