Android StackView type of Layout with Horizontal Swipe
Asked Answered
M

2

11

I am building an Android app that needs a "Card Deck" UI widget.

This is a request for an activity/layout example that can do the following:

1) Vertical Swipe Support:

List a deck of cards that can be vertically scrolled/swiped. StackView does this, but I'm open to any solution that works well (e.g., some CardUI project)

2) Horizontal Swipe Support:

For any card, there are 2 dictionary definitions:

  • If we do a left swipe - then we can see definition A.
  • If we do a right swipe, we see definition B
  • Horizontal swipe update does not update the entire screen layout, just the card that was swiped. So if I swipe Card2 to the right to see A2, I still have Card1 behind A2

Example:

[A1][Card1][B1]
[A2][Card2][B2]
[A3][Card3][B3]

I did see this related post here , the answers there provide some hints and reference info.. but unfortunately, I am still trying to figure it out.

Misogyny answered 9/4, 2014 at 6:1 Comment(0)
C
8

You have two possible approaches: take some open source project and adapt it to your needs, or otherwise, build your card swipe as a image slider from a detailed tutorial.

For the first option, have a look in Github, where you will find several small projects that do deck of cards with features usually on vertical or horizontal scrolling. I guess you may find interesting the following projects:

  • CardDeck: Deck of Cards for Android

  • DeckPicker: A complete android anki droid project with some additional feature of Card Preview within browser screen. In Preview screen card will be shown like review mode on card browser window.

In the end if the additional changes you made looks nice, it can be worth to submit a patch to the original project.

For the second alternative, for the case you prefer to implement it from scratch, then take simple steps, growing your project into more specific/complex details customizing it at your will. A fullscreen image slider would fit the bill, which would comprise the activity for the view page:

activity_fullscreen_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

And a Java class with a full screen viewer:

public class FullScreenImageAdapter extends PagerAdapter {

    private Activity _activity;
    private ArrayList<String> _imagePaths;
    private LayoutInflater inflater;

    // constructor
    public FullScreenImageAdapter(Activity activity,
            ArrayList<String> imagePaths) {
        this._activity = activity;
        this._imagePaths = imagePaths;
    }

    @Override
    public int getCount() {
        return this._imagePaths.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((RelativeLayout) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ImageView imgDisplay;
        Button btnClose;

        inflater = (LayoutInflater) _activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View viewLayout = inflater.inflate(R.layout.layout_fullscreen_image, container,
                false);

        imgDisplay = (ImageView) viewLayout.findViewById(R.id.imgDisplay);
        btnClose = (Button) viewLayout.findViewById(R.id.btnClose);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        Bitmap bitmap = BitmapFactory.decodeFile(_imagePaths.get(position), options);
        imgDisplay.setImageBitmap(bitmap);

        // close button click event
        btnClose.setOnClickListener(new View.OnClickListener() {           
            @Override
            public void onClick(View v) {
                _activity.finish();
            }
        });

        ((ViewPager) container).addView(viewLayout);

        return viewLayout;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        ((ViewPager) container).removeView((RelativeLayout) object);

    }
}

Then you achieve the image sliding horizontally, such as: enter image description here

And to add vertical motion, just include additional vertical layout:

main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="vertical" >

    <TextView

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="Swipe Demo"

        android:gravity="center"

        android:layout_margin="10dip" />

        <LinearLayout

            android:layout_width="fill_parent"

            android:layout_height="fill_parent"

            android:orientation="vertical"

            android:gravity="center">

                 <ImageView

                     android:layout_width="wrap_content"

                     android:layout_height="wrap_content"

                     android:layout_gravity="center"

                     android:gravity="center"

                     android:layout_margin="10dip"

                     android:id="@+id/image_place_holder"/>

           </LinearLayout>

</LinearLayout>

Which will allow you to slide things vertically:

enter image description here

At the end of the day, what you want to have is a grid, such as vertical and horizontal scrolling together. For that you have to combine both vertical and horizontal sliding into a table layout:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="fill_parent"
                  android:layout_height="wrap_content">

    <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="wrap_content"
                  android:layout_height="fill_parent">

         <TableLayout
                  android:id="@+id/amortization"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content">

              <TableRow
                  android:background="#ffff00">
                  <TextView
                       android:text="@string/amortization_1"
                       android:padding="3dip"/>
                  <TextView
                       android:text="@string/amortization_2"
                       android:padding="3dip"/>
                  <TextView
                       android:text="@string/amortization_3"
                       android:padding="3dip"/>
                  <TextView
                       android:text="@string/amortization_4"
                       android:padding="3dip"/>
                  <TextView
                       android:text="@string/amortization_5"
                       android:padding="3dip"/>
                  <TextView
                       android:text="@string/amortization_6"
                       android:padding="3dip"/>
                  <TextView
                       android:text="@string/amortization_7"
                       android:padding="3dip"/> 
              </TableRow>
         </TableLayout>
    </HorizontalScrollView>
</ScrollView>

Taking such steps and combining it, will allow you to achieve the effect you want.

Compensable answered 12/4, 2014 at 5:36 Comment(4)
Hi Avanz, thank you for your post and the screen shots - they help. What you are showing for horizontal swipe shows a full-screen change. I am trying to accomplish that a view in the foreground can be swiped horizontally, and the views behind it remain in place.Misogyny
Ah .. I see now in your notes that I should continue to iterate from the starter code you have listed and try combining different approaches to fit the requirements at hand. Thanks for sharing code & ideas on some different ways I can proceed with said approach.Misogyny
I was trying your suggestion to use this project github.com/cthibault/CardDeck , but I did not figure out how to run the code such that I can see the Card & Deck UI elements. I just see the Hello World screen and a non-working options menu. I did import both projects under that root folder into ADT, and did try running both. The test project seems to be only unit tests, without any UI to look at. I'm guessing you must be able to see something in your environment when you run the main CardDeck project ?Misogyny
Have you tried the DeckPicker yet? It even comes with detailed instructions - How to compile: code.google.com/p/ankidroid/wiki/Contribution#Source_codeCompensable
R
0

Try this sample code-

public class Main extends Activity {

    int position=0;
    LinearLayout full;
    Intent intent;

    public Integer[] images= {
                R.drawable.image1, R.drawable.image2,
                R.drawable.image3, R.drawable.image4,
                R.drawable.image5, R.drawable.image6
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);      
        full = (LinearLayout) findViewById(R.id.full);
        changeImage();
        ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this);
        full.setOnTouchListener(activitySwipeDetector);
    }



    private void changeImage(){
        full.setBackgroundResource(images[position]);
   }

    public class ActivitySwipeDetector implements View.OnTouchListener {

        static final String logTag = "ActivitySwipeDetector";
        static final int MIN_DISTANCE = 100;
        private float downX, upX;
        Activity activity;

        public ActivitySwipeDetector(Activity activity){
            this.activity = activity;
        }

        public void onRightToLeftSwipe(){
            Log.i(logTag, "RightToLeftSwipe!");
            if(position < images.length - 1){
                position++;
                 changeImage();
            }
    }

        public void onLeftToRightSwipe(){
            Log.i(logTag, "LeftToRightSwipe!");
            if(position > 0){
                position--;
                changeImage();
           }
        }

        public boolean onTouch(View v, MotionEvent event) {
            switch(event.getAction()){
                case MotionEvent.ACTION_DOWN: {
                    downX = event.getX();
                    return true;
                }
                case MotionEvent.ACTION_UP: {
                    upX = event.getX();

                    float deltaX = downX - upX;

                    // swipe horizontal?
                    if(Math.abs(deltaX) > MIN_DISTANCE){
                        // left or right
                        if(deltaX < 0) { this.onLeftToRightSwipe(); return true; }
                        if(deltaX > 0) { this.onRightToLeftSwipe(); return true; }
                    }
                    else {
                            Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                            return false; // We don't consume the event
                    }


                    return true;
                }
            }
            return false;
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override 
    public void onResume()
    {
        super.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override 
    public void onBackPressed() {
        super.onBackPressed();
    }       
}

Xml-

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/full"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerInside"
    android:orientation="vertical" />
Rebane answered 14/4, 2014 at 10:4 Comment(4)
I am trying this solution, but am getting an error on variable 'mThumbId' because the declaration is missing; happens at this line: if (position < mThumbId.length - 1). Can you tell me please how mThumbId should be set? .. I'm a bit confused because I thought mThumbId would be an int var to reference the ImageView reference that has been touched. Thanks for your postMisogyny
@gnB Replace mThumbId with images. See I edited post.Rebane
Hey @gnB did you get anything for this i'm facing the same problemCouchman
@Smith - this, ended up being more complicated than what I was ready for at the time. Hooking up swipe listeners I have found to be tricky. I think this would be more straight forward to revisit today - more knowledgable Android folks on SO now, and API has moved fwd. I would suggest to start a fresh post with the code you tried. As you can see it can get verbose quickly - so for my $0.02, I would say the code should crafted such that it's at a bare minimum.. where it will be easier for folks to identify where resolution is necessary. I would be interested to see how it goes. BestMisogyny

© 2022 - 2024 — McMap. All rights reserved.