0

I want to achive something like this

enter image description here

I already did something like this with a listview, but the problem I had is that after I select an item, it won't have the color it had when it was pressed. And also with a view pager I would have an extra container where I can put my fragments.

And if something like this would work it would be even better!

enter image description here

Android: Vertical ViewPager I've read this, looks ok, but I also want to have some items in the left side of the screens.

What do you think is the best way to do this? And also, I already have a navigation drawer, so a new one won't be the way to do it.

Community
  • 1
  • 1
Boldijar Paul
  • 5,175
  • 9
  • 42
  • 89

2 Answers2

1

I already did something like this with a listview, but the problem I had is that after I select an item, it won't have the color it had when it was pressed.

That is an easy fix. In the onClick(View arg0) method you add arg0.setSelected(true). Also remember to set the old view's selected state to false.

If there is a risk of views getting reused, you will need to keep track of which one is selected by change your list of data to a map and store a boolean value in the entries. However, if you want to scroll the list and detailed fragments together or the list is short enough as in your picture that won't be necessary.

Change your background color to a selector (drawable xml)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:color="@color/selected_color_resource"
        android:state_selected="true" />
    <item
        android:color="@color/color_resource" />  <!-- default -->
</selector>

The first matching state will be used so make sure default is the last item.

And also with a view pager I would have an extra container where I can put my fragments.

Yes use Brett's solution. Replace your detailed fragment with VerticalViewPager. You will need to set your activity (or whichever class that handles your list) to a ViewPager.SimpleOnPageChangeListener. So that when someone scroll the selected list item can be changed. You can do a lot of cool animaitons thanks to this nice method

onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
Adam Wigren
  • 383
  • 2
  • 11
0

try following example,

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <com.brandontate.BTGridPager.BTFragmentGridPager
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/fragmentGridPager"
            android:background="#ffff00"/>

</LinearLayout>

demo_fragment.xml

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical|center_horizontal"
            android:id="@+id/label"
            android:textSize="45sp"
            android:textColor="@android:color/black"
            android:layout_centerInParent="true"
            />

</RelativeLayout>

DemoFragment.java

package com.demo.app;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.brandontate.BTGridPager.BTFragmentGridPager;
import com.example.BTGridPager.R;

/**
 * BTGridPager
 *
 * @author Brandon Tate
 */
public class DemoFragment extends Fragment {

    TextView mLabel;

    BTFragmentGridPager.GridIndex mGridIndex;


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

        RelativeLayout layout = (RelativeLayout) inflater.inflate(R.layout.demo_fragment, container, false);
        mLabel = (TextView) layout.findViewById(R.id.label);
        setTxtRow(mGridIndex);

        return layout;

    }


    public void setTxtRow(BTFragmentGridPager.GridIndex gridIndex) {
        mLabel.setText("(" + gridIndex.getRow() + ", " + gridIndex.getCol() + ")");
    }

    public void setGridIndex(BTFragmentGridPager.GridIndex gridIndex){
        mGridIndex = gridIndex;
    }

}

Demo.java

package com.demo.app;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import com.brandontate.BTGridPager.BTFragmentGridPager;
import com.example.BTGridPager.R;

public class Demo extends FragmentActivity {

    private BTFragmentGridPager.FragmentGridPagerAdapter mFragmentGridPagerAdapter;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final BTFragmentGridPager mFragmentGridPager = (BTFragmentGridPager) findViewById(R.id.fragmentGridPager);

        mFragmentGridPagerAdapter = new BTFragmentGridPager.FragmentGridPagerAdapter() {
            @Override
            public int rowCount() {
                return 10;
            }

            @Override
            public int columnCount(int row) {
                return 10;
            }

            @Override
            public Fragment getItem(BTFragmentGridPager.GridIndex index) {
                DemoFragment panelFrag1 = new DemoFragment();
                panelFrag1.setGridIndex(index);

                return panelFrag1;
            }
        };

        mFragmentGridPager.setGridPagerAdapter(mFragmentGridPagerAdapter);
    }
}

BTVerticalPagerFragment.java

package com.brandontate.BTGridPager;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

/**
 * BTGridPager
 *
 * @author Brandon Tate
 */
public class BTVerticalPagerFragment extends Fragment {

    /** Pager adapter. */
    private FragmentStatePagerAdapter mVerticalPagerAdapter;

    /** Page change listener. */
    private ViewPager.OnPageChangeListener mPageChangeListener;

    /** The actual pager. */
    private BTVerticalPager mPager;

    /** The start page when the pager is loaded. */
    private int mStartPage = 0;

    private BTFragmentGridPager mGridPager;

    public BTVerticalPagerFragment(){

    }

//    public BTVerticalPagerFragment (BTFragmentGridPager gridPager) {
//
//        mGridPager = gridPager;
//    }



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

        LinearLayout view = new LinearLayout(getActivity());
        view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        view.setId(1);

        if (mGridPager != null) {
            mPager = new BTVerticalPager(getActivity(), mGridPager);
            mPager.setId(2);
            mPager.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
            mPager.setAdapter(mVerticalPagerAdapter);
            mPager.setCurrentItem(mStartPage);
            mPager.setOnPageChangeListener(mPageChangeListener);

            view.addView(mPager);
        }

        return view;
    }

    public FragmentStatePagerAdapter getVerticalPagerAdapter() {
        return mVerticalPagerAdapter;
    }

    public void setGridPager(BTFragmentGridPager gridPager){
        mGridPager = gridPager;
    }

    public void setVerticalPagerAdapter(FragmentStatePagerAdapter verticalPagerAdapter) {
        this.mVerticalPagerAdapter = verticalPagerAdapter;

        if (mPager != null)
            mPager.setAdapter(mVerticalPagerAdapter);
    }

    public void setPageChangeListener(ViewPager.OnPageChangeListener pageChangeListener) {
        this.mPageChangeListener = pageChangeListener;

        if (mPager != null)
            mPager.setOnPageChangeListener(mPageChangeListener);
    }

    public BTVerticalPager getPager() {
        return mPager;
    }

    public void setStartPage(int startPage) {
        this.mStartPage = startPage;
    }

}

BTVerticalPager.java

package com.brandontate.BTGridPager;


import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * BTGridPager
 *
 * @author Brandon Tate
 */
public class BTVerticalPager extends ViewPager {

    BTFragmentGridPager mGridPager;

    public BTVerticalPager(Context context, BTFragmentGridPager gridPager) {
        super(context);
        init();

        mGridPager = gridPager;
    }

    public BTVerticalPager(Context context, AttributeSet attrs, BTFragmentGridPager gridPager) {
        super(context, attrs);
        init();

        mGridPager = gridPager;
    }

    private void init(){

        // The majority of the magic happens here
        setPageTransformer(true, new VerticalPageTransformer());
        // The easiest way to get rid of the overscroll drawing that happens on the left and right
        setOverScrollMode(OVER_SCROLL_NEVER);
    }

    @Override
    protected void onPageScrolled(int position, float offset, int offsetPixels) {
        super.onPageScrolled(position, offset, offsetPixels);

        if (offset == 0  && mGridPager.shouldReset) {
            getRootView().post(new Runnable() {
                @Override
                public void run() {
                    mGridPager.shouldReset = false;
                    mGridPager.resetAdapter();
                }
            });
        }
    }

    private class VerticalPageTransformer implements ViewPager.PageTransformer {

        @Override
        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();

            view.setAlpha(1);
//
//            if (position < -1 && !mGridPager.shouldReset) { // [-Infinity,-1)
//                // This page is way off-screen to the left.
//                view.setAlpha(0);
//
//            }
            if (position >= -1 && position <= 1) { // [-1,1]
                // Counteract the default slide transition
                view.setTranslationX(pageWidth * -position);

                //set Y position to swipe in from top
                float yPosition = position * pageHeight;
                view.setTranslationY(yPosition);
            }
//            } else if (!mGridPager.shouldReset) { // (1,+Infinity]
//                // This page is way off-screen to the right.
//                view.setAlpha(0);
//            }
        }
    }

    /**
     * Swaps the X and Y coordinates of your touch event
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //swap the x and y coords of the touch event
        ev.setLocation(ev.getY(), ev.getX());

        return super.onTouchEvent(ev);
    }
}

BTFragmentGridPager.java

package com.brandontate.BTGridPager;


import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;


/**
 * BTGridPager
 *
 * The grid pager works by maintaining a view port that moves around a virtual grid.  The views to show in this viewport will be provided by the BTGridPagerAdapter.
 * When the a new page is selected the view port is updated to hold only the views surrounding it.
 *
 * @author Brandon Tate
 */
public class BTFragmentGridPager extends ViewPager {

    /** Bit flag for no wrapping. */
    public static final int GRID_WRAP_NONE = 0;

    /** Bit flag for left side wrapping. */
    public static final int GRID_WRAP_LEFT = 1 << 1;

    /** Bit flag for right side wrapping. */
    public static final int GRID_WRAP_RIGHT = 1 << 2;

    /** Bit flag for top side wrapping. */
    public static final int GRID_WRAP_UP = 1 << 3;

    /** Bit flag for bottom side wrapping. */
    public static final int GRID_WRAP_DOWN = 1 << 4;

    /** Wrapping flag. */
    private int mWrappingFlags = (GRID_WRAP_DOWN | GRID_WRAP_UP | GRID_WRAP_RIGHT);

    /** Bit flag for no resets. */
    public static final int GRID_RESET_NONE = 0;

    /** Bit flag for resetting to first column on row change. */
    public static final int GRID_RESET_ROW = 1 << 1;

    /** Bit flag for resetting to first row on column change. */
    public static final int GRID_RESET_COL = 1 << 2;

    /** Reset Flag. */
    private int mResetFlags = GRID_RESET_NONE;


    /** Current grid index. */
    public GridIndex mCurrentIndex = new GridIndex(0, 0);

    /** The fragment grid pager adapter for retrieving views. */
    private FragmentGridPagerAdapter mGridPagerAdapter;

    /** How many views to load on each side of the central view. */
    private int mGridPadding = 1;

    boolean shouldReset = false;

    public BTFragmentGridPager(Context context) {
        super(context);
        init();
    }


    /**
     * Used to inflate the Workspace from XML.
     *
     * @param context
     *            The application's context.
     * @param attrs
     *            The attribtues set containing the Workspace's customization values.
     */
    public BTFragmentGridPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public void init(){
        resetAdapter();
    }

    //*****************************************************
    //*
    //*         Helpers
    //*
    //*****************************************************

    private boolean canScrollLeft(){
        return  mGridPagerAdapter != null && !(mCurrentIndex.getCol() == 0 && (0 == (mWrappingFlags & GRID_WRAP_LEFT)));
    }

    private boolean canScrollRight(){
        return mGridPagerAdapter != null && !(mCurrentIndex.getCol() == (mGridPagerAdapter.columnCount(mCurrentIndex.getRow()) - 1) && (0 == (mWrappingFlags & GRID_WRAP_RIGHT)));
    }

    private boolean canScrollUp(){
        return  mGridPagerAdapter != null && !(mCurrentIndex.getRow() == 0 && (0 == (mWrappingFlags & GRID_WRAP_UP)));
    }

    private boolean canScrollDown(){
        return  mGridPagerAdapter != null && !(mCurrentIndex.getRow() == (mGridPagerAdapter.rowCount() - 1) && (0 == (mWrappingFlags & GRID_WRAP_DOWN)));
    }

    private GridIndex wrapGridIndex(GridIndex index){

        int newRow = index.getRow();

        if (newRow < 0)
            newRow = mGridPagerAdapter.rowCount() + newRow;
        else if(newRow >= mGridPagerAdapter.rowCount())
            newRow = (mGridPagerAdapter.rowCount() - newRow);

        int newCol = index.getCol();

        if (newCol < 0)
            newCol = mGridPagerAdapter.columnCount(newRow) + newCol;
        else if (newCol >= mGridPagerAdapter.columnCount(newRow))
            newCol = (mGridPagerAdapter.columnCount(newRow) - newCol);

        return new GridIndex(newRow, newCol);
    }

    /**
     *   Completely resets the adapter with updated current grid index.
     *
     *   No access modifier -- package-private
     */
    void resetAdapter(){

        // Clear old on page change listener to avoid unwanted actions
        setOnPageChangeListener(null);

        setAdapter(new FragmentGridHorizontalPagerAdapter(((FragmentActivity) getContext()).getSupportFragmentManager(), this));

        if (!canScrollLeft())
            setCurrentItem(0, false);
        else if(!canScrollRight())
            setCurrentItem(getAdapter().getCount() - 1);
        else
            setCurrentItem(mGridPadding, false);

        setOnPageChangeListener(new SimpleOnPageChangeListener(){

            @Override
            public void onPageSelected(int i) {

                // Default to wrapping index
                int newCol = mCurrentIndex.getCol() + ( -1 * (mGridPadding - i) );

                // Adjust for limited wrapping
                if (!canScrollLeft())
                    newCol = mCurrentIndex.getCol() + i;
                else if(!canScrollRight())
                    newCol--;

                mCurrentIndex = wrapGridIndex( new GridIndex( (0 != (mResetFlags & GRID_RESET_COL) ) ? 0 : mCurrentIndex.getRow() ,  newCol) );

                shouldReset = true;
            }
        });
    }

    @Override
    protected void onPageScrolled(int position, float offset, int offsetPixels) {
        super.onPageScrolled(position, offset, offsetPixels);

        if (offset == 0 && shouldReset) {
            getRootView().post(new Runnable() {
                @Override
                public void run() {
                    shouldReset = false;
                    resetAdapter();
                }
            });

        }
    }

    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        return false;
    }

    //*****************************************************
    //*
    //*         Getters/Setters
    //*
    //*****************************************************

    public FragmentGridPagerAdapter getGridPagerAdapter() {
        return mGridPagerAdapter;
    }

    public void setGridPagerAdapter(FragmentGridPagerAdapter gridPagerAdapter) {
        this.mGridPagerAdapter = gridPagerAdapter;
        resetAdapter();
    }

    public int getResetFlags() {
        return mResetFlags;
    }

    public void setResetFlags(int resetFlags) {
        this.mResetFlags = resetFlags;
    }

    public int getWrappingFlags() {
        return mWrappingFlags;
    }

    public void setWrappingFlags(int wrappingFlags) {
        this.mWrappingFlags = wrappingFlags;
    }


    //*****************************************************
    //*
    //*         Adapters
    //*
    //*****************************************************

    public class FragmentGridHorizontalPagerAdapter extends FragmentStatePagerAdapter{

        FragmentManager fm;
        BTFragmentGridPager mGridPager;

        public FragmentGridHorizontalPagerAdapter(FragmentManager fm, BTFragmentGridPager gridPager) {
            super(fm);

            this.fm = fm;
            this.mGridPager = gridPager;
        }

        @Override
        public Fragment getItem(int i) {

            if (mGridPagerAdapter == null)
                return new Fragment();

            // Figure out if this is our vertical pager
            boolean vpFragFlag = false;

            if (!canScrollLeft() &&  i == 0)
                vpFragFlag = true;
            else if(!canScrollRight() && i == (getAdapter().getCount() - 1))
                vpFragFlag = true;
            else if(canScrollLeft() && canScrollRight())
                vpFragFlag = (i == mGridPadding);

            if (vpFragFlag){

                BTVerticalPagerFragment vpFragment = new BTVerticalPagerFragment();
                vpFragment.setGridPager(mGridPager);

                vpFragment.setVerticalPagerAdapter(new FragmentStatePagerAdapter(fm){
                    @Override
                    public Fragment getItem(int i) {

                        if (mGridPagerAdapter == null)
                            return new Fragment();

                        int newCol = mCurrentIndex.getCol();

                        // If it's not the middle one and we need a row reset
                        if (i != mGridPadding && (0 != (mResetFlags & GRID_RESET_ROW) ))
                            newCol = 0;


                        // Default my view index wrapped
                        GridIndex viewIndex = wrapGridIndex(wrapGridIndex( new GridIndex( mCurrentIndex.getRow() + ( -1 * (mGridPadding - i) ), newCol ) ) );

                        // Adjust for limited wrapping
                        if (!canScrollUp() || !canScrollDown()) {
                            if (i == this.getCount() - 1)
                                viewIndex.setRow(mGridPagerAdapter.rowCount() - 1);
                            else
                                viewIndex.setRow(mCurrentIndex.getRow() + i);
                        }

                        return mGridPagerAdapter.getItem(viewIndex);
                    }

                    @Override
                    public int getCount() {
                        return (mGridPadding * 2) + 1;
                    }

                    public int getItemPosition(Object object){
                        return POSITION_NONE;
                    }
                });


                if (!canScrollUp())
                    vpFragment.setStartPage(0);
                else if(!canScrollDown())
                    vpFragment.setStartPage(vpFragment.getVerticalPagerAdapter().getCount() - 1);
                else
                vpFragment.setStartPage(mGridPadding);

                vpFragment.setPageChangeListener(new SimpleOnPageChangeListener() {

                    @Override
                    public void onPageSelected(int i) {
                        int newRow = mCurrentIndex.getRow() + ( -1 * (mGridPadding - i) );

                        if (!canScrollUp())
                            newRow = mCurrentIndex.getRow() + i;
                        else if(!canScrollDown())
                            newRow--;

                        int newCol = mCurrentIndex.getCol();

                        // If it's not the middle one and we need a row reset
                        if (i != mGridPadding && (0 != (mResetFlags & GRID_RESET_ROW) ))
                            newCol = 0;

                        mCurrentIndex = wrapGridIndex(new GridIndex(newRow, newCol));

                        shouldReset = true;

                    }

                });

                return vpFragment;
            }
            else{

                // Default my view index wrapped
                GridIndex viewIndex = wrapGridIndex(new GridIndex((0 != (mResetFlags & GRID_RESET_COL) ) ? 0 : mCurrentIndex.getRow(), mCurrentIndex.getCol() + ( -1 * (mGridPadding - i) ) ) );

                // Adjust for limited wrapping
                if (!canScrollLeft() || !canScrollRight())
                    viewIndex.setCol(mCurrentIndex.getCol() + i);

                return mGridPagerAdapter.getItem(viewIndex);
            }
        }

        public int getItemPosition(Object object){
            return POSITION_NONE;
        }

        @Override
        public int getCount() {
            return (mGridPadding * 2) + 1;
        }

    }

    public interface FragmentGridPagerAdapter{

        public int rowCount();
        public int columnCount(int row);
        public Fragment getItem(GridIndex index);

    }

    //*****************************************************
    //*
    //*         Data Types
    //*
    //*****************************************************

    public class GridIndex{

        private int mRow = 0, mCol = 0;

        public GridIndex(int row, int col){

            mRow = row;
            mCol = col;

        }

        public int getRow() {
            return mRow;
        }

        public void setRow(int row) {
            this.mRow = row;
        }

        public int getCol() {
            return mCol;
        }

        public void setCol(int col) {
            this.mCol = col;
        }

        @Override
        public String toString() {
            return "GridIndex{" +
                    "mRow=" + mRow +
                    ", mCol=" + mCol +
                    '}';
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            GridIndex gridIndex = (GridIndex) o;

            if (mCol != gridIndex.mCol) return false;
            if (mRow != gridIndex.mRow) return false;

            return true;
        }
    }
}
Ganpat Kaliya
  • 1,234
  • 2
  • 9
  • 16